{
  "openapi": "3.1.0",
  "info": {
    "title": "llmsmap Public REST API",
    "version": "1.0.0",
    "summary": "Read-only access to the llmsmap website catalogs",
    "description": "Query approved websites that publish llms.txt and inspect the status of a submitted website. The llmsmap.me host is scoped to the international catalog; llmsmap.ru includes Russian and international catalog segments. llms.txt is an emerging convention, and inclusion in this directory does not guarantee crawling, indexing, ranking, citation, or inclusion in AI-generated answers.",
    "termsOfService": "https://llmsmap.me/legal/terms/",
    "contact": {
      "name": "llmsmap support",
      "url": "https://llmsmap.me/contacts/"
    },
    "x-distribution-repository": "https://github.com/arseniigruzdev/llmsmap-distribution"
  },
  "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
  "servers": [
    {
      "url": "https://llmsmap.me",
      "description": "International English catalog"
    },
    {
      "url": "https://llmsmap.ru",
      "description": "Russian catalog with Russian and international segments"
    }
  ],
  "externalDocs": {
    "description": "Human-readable API documentation",
    "url": "https://llmsmap.me/api-docs/"
  },
  "tags": [
    {
      "name": "Directory",
      "description": "Approved public catalog entries"
    },
    {
      "name": "Submissions",
      "description": "Privacy-preserving submission status lookup"
    }
  ],
  "paths": {
    "/api/directory": {
      "get": {
        "operationId": "listDirectorySites",
        "summary": "List approved websites",
        "description": "Returns up to 100 approved websites for the edition selected by the request host. Use /api/catalog for filtering and pagination.",
        "tags": ["Directory"],
        "security": [],
        "responses": {
          "200": {
            "description": "Approved public catalog entries",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Maximum requests in the current window when supplied by the service",
                "schema": { "type": "integer" }
              }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/DirectoryResponse" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/catalog": {
      "get": {
        "operationId": "searchCatalog",
        "summary": "Search and paginate the catalog",
        "description": "Returns catalog entries from the edition selected by the request host. The edition cannot be overridden by a query parameter.",
        "tags": ["Directory"],
        "security": [],
        "parameters": [
          {
            "name": "search",
            "in": "query",
            "description": "Product name, domain, slug, or notes substring",
            "schema": { "type": "string", "maxLength": 200 }
          },
          {
            "name": "segment",
            "in": "query",
            "description": "Catalog segment. llmsmap.me always resolves this to international.",
            "schema": {
              "type": "string",
              "enum": ["russian", "international", "all"]
            }
          },
          {
            "name": "country_code",
            "in": "query",
            "description": "ISO 3166-1 alpha-2 country code",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z]{2}$",
              "examples": ["DE"]
            }
          },
          {
            "name": "sort",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": ["recommended", "newest", "ai_ready", "alphabetical"],
              "default": "newest"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "default": 1 }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 24 }
          }
        ],
        "responses": {
          "200": {
            "description": "Filtered catalog page",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/CatalogResponse" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/submission/{id}/status": {
      "get": {
        "operationId": "getSubmissionStatus",
        "summary": "Get a submission status",
        "description": "Returns safe public status fields only. The private status token is issued when a website is submitted and must not be shared.",
        "tags": ["Submissions"],
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "integer", "minimum": 1 }
          },
          {
            "name": "token",
            "in": "query",
            "required": true,
            "description": "Private status token returned after submission",
            "schema": { "type": "string", "minLength": 1 }
          }
        ],
        "responses": {
          "200": {
            "description": "Current submission status",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/SubmissionStatus" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "403": {
            "description": "Status token is missing",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "404": {
            "description": "Submission not found or token is invalid",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "CatalogItem": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "product_name",
          "website_url",
          "llms_txt_url",
          "llms_full_txt_url",
          "llms_txt_tokens",
          "llms_full_txt_tokens",
          "domain",
          "slug",
          "listing_score",
          "ai_readiness_score",
          "published_at",
          "is_featured"
        ],
        "properties": {
          "id": { "type": "integer", "minimum": 1 },
          "product_name": { "type": "string" },
          "website_url": { "type": "string", "format": "uri" },
          "llms_txt_url": { "type": "string", "format": "uri" },
          "llms_full_txt_url": { "type": ["string", "null"], "format": "uri" },
          "llms_txt_tokens": { "type": ["integer", "null"], "minimum": 0 },
          "llms_full_txt_tokens": { "type": ["integer", "null"], "minimum": 0 },
          "domain": { "type": ["string", "null"] },
          "slug": { "type": ["string", "null"] },
          "listing_score": { "type": ["integer", "null"] },
          "ai_readiness_score": { "type": ["integer", "null"], "minimum": 0, "maximum": 100 },
          "published_at": { "type": ["string", "null"] },
          "is_featured": { "type": "boolean" }
        }
      },
      "DirectoryResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": ["items"],
        "properties": {
          "items": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/CatalogItem" }
          }
        }
      },
      "CatalogResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": ["items", "pagination", "filters", "counts"],
        "properties": {
          "items": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/CatalogItem" }
          },
          "pagination": { "$ref": "#/components/schemas/Pagination" },
          "filters": { "$ref": "#/components/schemas/AppliedFilters" },
          "counts": { "$ref": "#/components/schemas/CatalogCounts" }
        }
      },
      "Pagination": {
        "type": "object",
        "additionalProperties": false,
        "required": ["page", "limit", "total", "total_pages", "has_next", "has_previous"],
        "properties": {
          "page": { "type": "integer", "minimum": 1 },
          "limit": { "type": "integer", "minimum": 1, "maximum": 100 },
          "total": { "type": "integer", "minimum": 0 },
          "total_pages": { "type": "integer", "minimum": 1 },
          "has_next": { "type": "boolean" },
          "has_previous": { "type": "boolean" }
        }
      },
      "AppliedFilters": {
        "type": "object",
        "additionalProperties": false,
        "required": ["active_segment", "active_sort", "search"],
        "properties": {
          "active_segment": {
            "type": "string",
            "enum": ["russian", "international", "all"]
          },
          "active_sort": {
            "type": "string",
            "enum": ["recommended", "newest", "ai_ready", "alphabetical"]
          },
          "search": { "type": ["string", "null"] }
        }
      },
      "CatalogCounts": {
        "type": "object",
        "additionalProperties": false,
        "required": ["russian", "international", "all", "with_llms_full_txt", "ai_ready", "total_tokens"],
        "properties": {
          "russian": { "type": "integer", "minimum": 0 },
          "international": { "type": "integer", "minimum": 0 },
          "all": { "type": "integer", "minimum": 0 },
          "with_llms_full_txt": { "type": "integer", "minimum": 0 },
          "ai_ready": { "type": "integer", "minimum": 0 },
          "total_tokens": { "type": "integer", "minimum": 0 }
        }
      },
      "SubmissionStatus": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "product_name",
          "website_url",
          "llms_txt_url",
          "llms_full_txt_url",
          "status",
          "ai_reviewed",
          "ai_decision",
          "ai_details",
          "ai_reviewed_at"
        ],
        "properties": {
          "id": { "type": "integer", "minimum": 1 },
          "product_name": { "type": "string" },
          "website_url": { "type": "string", "format": "uri" },
          "llms_txt_url": { "type": "string", "format": "uri" },
          "llms_full_txt_url": { "type": ["string", "null"], "format": "uri" },
          "status": { "type": "string", "enum": ["pending", "approved", "declined"] },
          "ai_reviewed": { "type": "boolean" },
          "ai_decision": { "type": ["string", "null"], "enum": ["approved", "declined", null] },
          "ai_details": {
            "type": ["object", "null"],
            "additionalProperties": true
          },
          "ai_reviewed_at": { "type": ["string", "null"] }
        }
      },
      "Error": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": { "type": "string" },
          "details": {}
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Invalid request",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded",
        "headers": {
          "Retry-After": {
            "required": true,
            "schema": { "type": "integer", "minimum": 1 }
          }
        },
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      }
    }
  }
}
