{
  "openapi": "3.0.3",
  "info": {
    "title": "Codebutor API",
    "version": "1.0.0",
    "description": "Read-only JSON API over Codebutor's catalog of trending repositories and awesome lists, plus contribution signals, search, similar repositories and dependency health.\n\nAll endpoints work anonymously under a shared per-IP rate limit. Send an API key in the X-Api-Key header for a much higher per-minute limit and a daily quota; manage keys at /api-keys. Exceeding a limit returns 429 with a Retry-After header. Responses for keyed requests include X-Api-Quota-Remaining."
  },
  "servers": [ { "url": "/" } ],
  "security": [ {}, { "ApiKey": [] } ],
  "tags": [
    { "name": "Trending" },
    { "name": "Awesome lists" },
    { "name": "Repositories" },
    { "name": "Contribute" },
    { "name": "Search" },
    { "name": "Dependency health" },
    { "name": "Badges" }
  ],
  "paths": {
    "/api/v1/trending": {
      "get": {
        "tags": [ "Trending" ],
        "summary": "Trending repositories (HTML table fragment)",
        "description": "Returns the rendered table rows the site uses, not JSON. Use /api/v1/trending/search for JSON.",
        "parameters": [
          { "$ref": "#/components/parameters/Since" },
          { "$ref": "#/components/parameters/Page" },
          { "$ref": "#/components/parameters/PageSize" },
          { "name": "language", "in": "query", "schema": { "type": "string" } },
          { "name": "search", "in": "query", "schema": { "type": "string" } },
          { "name": "sortBy", "in": "query", "schema": { "type": "string", "enum": [ "rank", "name", "stars", "forks", "language" ], "default": "rank" } },
          { "name": "minStars", "in": "query", "schema": { "type": "integer", "default": 0 } },
          { "name": "maxStars", "in": "query", "schema": { "type": "integer" } }
        ],
        "responses": {
          "200": { "description": "HTML fragment", "content": { "text/html": { "schema": { "type": "string" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/api/v1/trending/search": {
      "get": {
        "tags": [ "Trending" ],
        "summary": "Search the current trending list",
        "parameters": [
          { "name": "query", "in": "query", "required": true, "schema": { "type": "string" } },
          { "$ref": "#/components/parameters/Since" },
          { "$ref": "#/components/parameters/Page" },
          { "name": "pageSize", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 10 } }
        ],
        "responses": {
          "200": {
            "description": "A page of trending repositories",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TrendingSearchPage" } } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/api/v1/trending/stats": {
      "get": {
        "tags": [ "Trending" ],
        "summary": "Collection statistics and top languages",
        "responses": {
          "200": { "description": "Stats", "content": { "application/json": { "schema": { "type": "object" } } } },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/api/v1/trending/languages": {
      "get": {
        "tags": [ "Trending" ],
        "summary": "Languages by share of trending repositories",
        "parameters": [ { "$ref": "#/components/parameters/Since" } ],
        "responses": {
          "200": { "description": "Languages", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "object", "properties": { "language": { "type": "string" }, "count": { "type": "integer" }, "averageStars": { "type": "number" }, "totalStars": { "type": "integer" } } } } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/api/v1/trending/technologies-filter": {
      "get": {
        "tags": [ "Trending" ],
        "summary": "Languages available as filters",
        "parameters": [ { "$ref": "#/components/parameters/Since" } ],
        "responses": {
          "200": { "description": "Language names", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string" } } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/api/v1/trending/job-executions": {
      "get": {
        "tags": [ "Trending" ],
        "summary": "Recent collector runs",
        "parameters": [
          { "$ref": "#/components/parameters/Page" },
          { "name": "pageSize", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 10 } }
        ],
        "responses": {
          "200": { "description": "A page of job executions", "content": { "application/json": { "schema": { "type": "object" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/api/v1/awesome/lists": {
      "get": {
        "tags": [ "Awesome lists" ],
        "summary": "All awesome lists with health scores",
        "responses": {
          "200": { "description": "Lists", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "object" } } } } },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/api/v1/awesome/stats": {
      "get": {
        "tags": [ "Awesome lists" ],
        "summary": "Awesome list statistics",
        "responses": {
          "200": { "description": "Stats", "content": { "application/json": { "schema": { "type": "object" } } } },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/api/v1/awesome/{listId}": {
      "get": {
        "tags": [ "Awesome lists" ],
        "summary": "Repositories in an awesome list",
        "description": "Send Accept: application/json to receive JSON; otherwise an HTML table fragment is returned. Omit listId for repositories across all lists.",
        "parameters": [
          { "name": "listId", "in": "path", "required": true, "schema": { "type": "integer" } },
          { "$ref": "#/components/parameters/Page" },
          { "name": "pageSize", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 } },
          { "name": "search", "in": "query", "schema": { "type": "string" } },
          { "name": "sortBy", "in": "query", "schema": { "type": "string", "default": "created" } },
          { "name": "sortOrder", "in": "query", "schema": { "type": "string", "enum": [ "asc", "desc" ], "default": "desc" } },
          { "name": "category", "in": "query", "schema": { "type": "string" } },
          { "name": "hideInactive", "in": "query", "schema": { "type": "boolean", "default": false } }
        ],
        "responses": {
          "200": { "description": "Page of repositories", "content": { "application/json": { "schema": { "type": "object" } } } },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/api/v1/awesome/{listId}/categories": {
      "get": {
        "tags": [ "Awesome lists" ],
        "summary": "Section headings of an awesome list",
        "parameters": [ { "name": "listId", "in": "path", "required": true, "schema": { "type": "integer" } } ],
        "responses": {
          "200": { "description": "Categories", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "object" } } } } },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/api/v1/repo/{user}/{repoName}": {
      "get": {
        "tags": [ "Repositories" ],
        "summary": "One repository, with activity status and contribution signals",
        "parameters": [
          { "name": "user", "in": "path", "required": true, "schema": { "type": "string" } },
          { "name": "repoName", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": { "description": "The repository", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Repository" } } } },
          "404": { "description": "Not tracked" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/api/v1/repo/{user}/{repoName}/rankings": {
      "get": {
        "tags": [ "Repositories" ],
        "summary": "Trending placements over the last 90 observations",
        "parameters": [
          { "name": "user", "in": "path", "required": true, "schema": { "type": "string" } },
          { "name": "repoName", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": { "description": "Rankings", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "object", "properties": { "since": { "type": "string" }, "sinceBasedRank": { "type": "integer" }, "sinceBasedStars": { "type": "integer" }, "date": { "type": "string", "format": "date" } } } } } } },
          "404": { "description": "Not tracked" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/api/v1/repo/{user}/{repoName}/history": {
      "get": {
        "tags": [ "Repositories" ],
        "summary": "Star and fork history (one point per day, oldest first)",
        "parameters": [
          { "name": "user", "in": "path", "required": true, "schema": { "type": "string" } },
          { "name": "repoName", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": { "description": "History", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "object", "properties": { "date": { "type": "string", "format": "date" }, "stars": { "type": "integer" }, "forks": { "type": "integer" } } } } } } },
          "404": { "description": "Not tracked" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/api/v1/contribute": {
      "get": {
        "tags": [ "Contribute" ],
        "summary": "Projects where a newcomer can help right now",
        "description": "Repositories with confirmed good-first-issue / help-wanted counts, ranked by a 0-100 welcome score built from recent activity, newcomer issues, a CONTRIBUTING guide and a license. Repositories whose issues haven't been collected yet are left out.",
        "parameters": [
          { "name": "language", "in": "query", "schema": { "type": "string" } },
          { "name": "search", "in": "query", "schema": { "type": "string" } },
          { "name": "goodFirstOnly", "in": "query", "schema": { "type": "boolean", "default": true } },
          { "name": "activeOnly", "in": "query", "description": "Only repositories pushed in the last 6 months", "schema": { "type": "boolean", "default": true } },
          { "name": "guideOnly", "in": "query", "schema": { "type": "boolean", "default": false } },
          { "name": "sort", "in": "query", "schema": { "type": "string", "enum": [ "friendly", "goodfirst", "stars", "active" ], "default": "friendly" } },
          { "$ref": "#/components/parameters/Page" },
          { "name": "pageSize", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 } }
        ],
        "responses": {
          "200": { "description": "A page of opportunities", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContributePage" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/api/v1/search": {
      "get": {
        "tags": [ "Search" ],
        "summary": "Search repositories in plain words",
        "description": "Relevance search over names, topics, tags, languages and descriptions that understands related terms (for example db and database, fast and performance).",
        "parameters": [
          { "name": "q", "in": "query", "required": true, "schema": { "type": "string", "maxLength": 200 } },
          { "name": "language", "in": "query", "schema": { "type": "string" } },
          { "$ref": "#/components/parameters/Page" },
          { "name": "pageSize", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 } }
        ],
        "responses": {
          "200": { "description": "Ranked hits", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SearchPage" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/api/v1/similar/{id}": {
      "get": {
        "tags": [ "Search" ],
        "summary": "Repositories similar to one repository",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "description": "Codebutor repository id", "schema": { "type": "integer" } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 50, "default": 10 } }
        ],
        "responses": {
          "200": { "description": "Similar repositories, most similar first", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/SearchHit" } } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "404": { "description": "Unknown repository id" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/api/v1/scan": {
      "post": {
        "tags": [ "Dependency health" ],
        "summary": "Check a dependency manifest",
        "description": "Accepts package.json, .csproj / Directory.Packages.props / packages.config, requirements.txt or go.mod (max 512 KB, first 150 dependencies). Each package is mapped to its GitHub repository and classified Active / Slowing / Inactive / Archived. Tighter rate limit than other endpoints. Nothing you send is stored.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": { "schema": { "type": "object", "required": [ "content" ], "properties": { "fileName": { "type": "string", "example": "package.json" }, "content": { "type": "string" } } } },
            "text/plain": { "schema": { "type": "string" } }
          }
        },
        "parameters": [ { "name": "file", "in": "query", "description": "File name when sending text/plain", "schema": { "type": "string" } } ],
        "responses": {
          "200": { "description": "Health report", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ScanReport" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/badge/{user}/{repoName}.svg": {
      "get": {
        "tags": [ "Badges" ],
        "summary": "Trending rank (or star count) badge",
        "parameters": [
          { "name": "user", "in": "path", "required": true, "schema": { "type": "string" } },
          { "name": "repoName", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": { "200": { "description": "SVG badge", "content": { "image/svg+xml": { "schema": { "type": "string" } } } } }
      }
    },
    "/badge/{user}/{repoName}/activity.svg": {
      "get": {
        "tags": [ "Badges" ],
        "summary": "Maintenance status badge (active, slowing, inactive, archived)",
        "parameters": [
          { "name": "user", "in": "path", "required": true, "schema": { "type": "string" } },
          { "name": "repoName", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": { "200": { "description": "SVG badge", "content": { "image/svg+xml": { "schema": { "type": "string" } } } } }
      }
    },
    "/badge/{user}/{repoName}/issues.svg": {
      "get": {
        "tags": [ "Badges" ],
        "summary": "Open good first issues badge",
        "parameters": [
          { "name": "user", "in": "path", "required": true, "schema": { "type": "string" } },
          { "name": "repoName", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": { "200": { "description": "SVG badge", "content": { "image/svg+xml": { "schema": { "type": "string" } } } } }
      }
    },
    "/badge/{user}/{repoName}/stars.svg": {
      "get": {
        "tags": [ "Badges" ],
        "summary": "Star count badge with a 90-day growth sparkline",
        "parameters": [
          { "name": "user", "in": "path", "required": true, "schema": { "type": "string" } },
          { "name": "repoName", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": { "200": { "description": "SVG badge", "content": { "image/svg+xml": { "schema": { "type": "string" } } } } }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKey": { "type": "apiKey", "in": "header", "name": "X-Api-Key", "description": "Optional. Create keys at /api-keys." }
    },
    "parameters": {
      "Since": { "name": "since", "in": "query", "schema": { "type": "string", "enum": [ "daily", "weekly", "monthly" ], "default": "weekly" } },
      "Page": { "name": "page", "in": "query", "schema": { "type": "integer", "minimum": 1, "default": 1 } },
      "PageSize": { "name": "pageSize", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 } }
    },
    "responses": {
      "BadRequest": { "description": "Invalid parameters" },
      "TooManyRequests": {
        "description": "Rate limit or daily quota exceeded",
        "headers": { "Retry-After": { "description": "Seconds until the limit resets", "schema": { "type": "integer" } } },
        "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" } } } } }
      }
    },
    "schemas": {
      "Repository": {
        "type": "object",
        "properties": {
          "user": { "type": "string" },
          "repository": { "type": "string" },
          "url": { "type": "string" },
          "language": { "type": "string" },
          "description": { "type": "string" },
          "tags": { "type": "string" },
          "stars": { "type": "integer" },
          "forks": { "type": "integer" },
          "createdAt": { "type": "string", "format": "date-time" },
          "repositoryLastUpdate": { "type": "string", "format": "date-time" },
          "isArchived": { "type": "boolean" },
          "activityStatus": { "type": "string", "enum": [ "Active", "Slowing", "Inactive", "Archived", "Unknown" ] },
          "openIssuesCount": { "type": "integer", "nullable": true, "description": "Null until the collector has checked the repository's issues" },
          "goodFirstIssueCount": { "type": "integer", "nullable": true },
          "helpWantedCount": { "type": "integer", "nullable": true },
          "license": { "type": "string" },
          "topics": { "type": "string", "description": "Comma-separated, lowercase" },
          "hasContributingGuide": { "type": "boolean", "nullable": true },
          "friendlinessScore": { "type": "integer", "nullable": true, "minimum": 0, "maximum": 100 }
        }
      },
      "TrendingSearchPage": {
        "type": "object",
        "properties": {
          "data": { "type": "array", "items": { "type": "object" } },
          "query": { "type": "string" },
          "since": { "type": "string" },
          "totalCount": { "type": "integer" },
          "currentPage": { "type": "integer" },
          "pageSize": { "type": "integer" },
          "totalPages": { "type": "integer" }
        }
      },
      "ContributePage": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": { "type": "integer" },
                "user": { "type": "string" },
                "repository": { "type": "string" },
                "url": { "type": "string" },
                "language": { "type": "string" },
                "description": { "type": "string" },
                "stars": { "type": "integer" },
                "openIssues": { "type": "integer" },
                "goodFirstIssues": { "type": "integer" },
                "helpWanted": { "type": "integer" },
                "license": { "type": "string" },
                "hasContributingGuide": { "type": "boolean" },
                "activityStatus": { "type": "string" },
                "friendlinessScore": { "type": "integer", "minimum": 0, "maximum": 100 },
                "goodFirstIssuesUrl": { "type": "string" }
              }
            }
          },
          "totalCount": { "type": "integer" },
          "currentPage": { "type": "integer" },
          "pageSize": { "type": "integer" },
          "totalPages": { "type": "integer" }
        }
      },
      "SearchHit": {
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "user": { "type": "string" },
          "repository": { "type": "string" },
          "url": { "type": "string" },
          "language": { "type": "string" },
          "description": { "type": "string" },
          "stars": { "type": "integer" },
          "activityStatus": { "type": "string" },
          "score": { "type": "number" }
        }
      },
      "SearchPage": {
        "type": "object",
        "properties": {
          "query": { "type": "string" },
          "data": { "type": "array", "items": { "$ref": "#/components/schemas/SearchHit" } },
          "currentPage": { "type": "integer" },
          "pageSize": { "type": "integer" }
        }
      },
      "ScanReport": {
        "type": "object",
        "properties": {
          "ecosystem": { "type": "string", "enum": [ "Npm", "NuGet", "PyPI", "Go" ] },
          "truncated": { "type": "boolean" },
          "summary": {
            "type": "object",
            "properties": {
              "total": { "type": "integer" },
              "active": { "type": "integer" },
              "slowing": { "type": "integer" },
              "inactive": { "type": "integer" },
              "archived": { "type": "integer" },
              "untracked": { "type": "integer", "description": "Has a GitHub repository Codebutor doesn't track" },
              "unresolved": { "type": "integer", "description": "No GitHub repository found" },
              "atRisk": { "type": "integer", "description": "inactive + archived" }
            }
          },
          "dependencies": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": { "type": "string" },
                "version": { "type": "string", "nullable": true },
                "scope": { "type": "string", "enum": [ "runtime", "dev", "indirect" ] },
                "match": { "type": "string", "enum": [ "tracked", "untracked", "unresolved" ] },
                "githubUrl": { "type": "string", "nullable": true },
                "codebutorUrl": { "type": "string", "nullable": true },
                "activity": { "type": "string" },
                "detail": { "type": "string" },
                "stars": { "type": "integer", "nullable": true },
                "starChange": { "type": "integer", "nullable": true },
                "starChangeDays": { "type": "integer", "nullable": true }
              }
            }
          }
        }
      }
    }
  }
}
