openapi: 3.1.0
info:
  title: AgentExplorio Public API
  version: 1.0.0
  description: |
    Read-only access to the AgentExplorio directory of AI agents, MCP servers and agent tools.
    Search uses the listing index only; no AI is involved in ranking.
    Free API keys. Limits: 60 requests per minute and 10,000 per day per key.
    Unverified listings are included and labeled; filter with `verified=true`.
  contact:
    email: hello@agentexplorio.com
  termsOfService: https://agentexplorio.com/terms
servers:
  - url: https://agentexplorio.com/api/v1
security:
  - apiKey: []
paths:
  /search:
    get:
      operationId: searchAgents
      summary: Search agents by task or keywords
      parameters:
        - { name: q, in: query, required: true, schema: { type: string, minLength: 2, maxLength: 200 }, description: "What you need done, e.g. 'client reporting for agencies'" }
        - { name: industry, in: query, schema: { type: string }, description: Industry slug, e.g. marketing-agencies }
        - { name: type, in: query, schema: { type: string, enum: [agent, assistant, mcp_tool, builder_tool, workflow_template] } }
        - { name: price, in: query, schema: { type: string, enum: [free, freemium, paid, open_source, unknown] } }
        - { name: setup, in: query, schema: { type: string, enum: [no_code, developer, unknown] } }
        - { name: works_with, in: query, schema: { type: string }, description: "Integration or protocol, e.g. Slack, HubSpot, MCP, A2A" }
        - { name: verified, in: query, schema: { type: boolean, default: false }, description: Only owner-verified or higher }
        - { name: page, in: query, schema: { type: integer, minimum: 1, default: 1 } }
        - { name: per_page, in: query, schema: { type: integer, minimum: 1, maximum: 50, default: 20 } }
      responses:
        "200":
          description: Matching listings
          content:
            application/json:
              schema:
                type: object
                required: [query, total, page, per_page, results]
                properties:
                  query: { type: string }
                  total: { type: integer }
                  page: { type: integer }
                  per_page: { type: integer }
                  results: { type: array, items: { $ref: "#/components/schemas/ListingSummary" } }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /agents/{slug}:
    get:
      operationId: getAgent
      summary: Full public listing for one agent
      parameters:
        - { name: slug, in: path, required: true, schema: { type: string } }
      responses:
        "200":
          description: Listing
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Listing" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /compare:
    get:
      operationId: compareAgents
      summary: Side-by-side facts for up to 3 agents
      parameters:
        - { name: slugs, in: query, required: true, schema: { type: string }, description: "Comma-separated, 2 to 3 slugs" }
      responses:
        "200":
          description: Listings in the order requested
          content:
            application/json:
              schema:
                type: object
                properties:
                  results: { type: array, maxItems: 3, items: { $ref: "#/components/schemas/Listing" } }
        "400": { $ref: "#/components/responses/BadRequest" }
        "429": { $ref: "#/components/responses/RateLimited" }
components:
  securitySchemes:
    apiKey:
      type: apiKey
      in: header
      name: X-API-Key
  responses:
    BadRequest:
      description: Invalid parameters
      content: { application/json: { schema: { $ref: "#/components/schemas/Error" } } }
    Unauthorized:
      description: Missing or invalid API key
      content: { application/json: { schema: { $ref: "#/components/schemas/Error" } } }
    NotFound:
      description: No live listing with that slug
      content: { application/json: { schema: { $ref: "#/components/schemas/Error" } } }
    RateLimited:
      description: Over the per-minute or per-day limit. See Retry-After header.
      headers:
        Retry-After: { schema: { type: integer }, description: Seconds to wait }
      content: { application/json: { schema: { $ref: "#/components/schemas/Error" } } }
  schemas:
    Error:
      type: object
      required: [error]
      properties:
        error: { type: string }
    Verification:
      type: string
      enum: [unverified, owner_verified, demo_verified, user_verified, customer_verified]
    ListingSummary:
      type: object
      required: [slug, name, summary, type, url, page_url, verification]
      properties:
        slug: { type: string }
        name: { type: string }
        summary: { type: string, description: One-sentence summary }
        type: { type: string }
        url: { type: string, format: uri, description: The agent's own site }
        page_url: { type: string, format: uri, description: The AgentExplorio listing page }
        logo_url: { type: [string, "null"], format: uri }
        price_model: { type: string }
        setup: { type: string }
        works_with: { type: array, items: { type: string } }
        verification: { $ref: "#/components/schemas/Verification" }
        last_checked_at: { type: [string, "null"], format: date-time }
    Listing:
      allOf:
        - $ref: "#/components/schemas/ListingSummary"
        - type: object
          properties:
            description: { type: string }
            monthly_cost:
              type: [object, "null"]
              description: Null when not confirmed
              properties:
                min: { type: [integer, "null"] }
                max: { type: [integer, "null"] }
                currency: { type: string }
                source_url: { type: [string, "null"], format: uri }
            free_limits: { type: [string, "null"] }
            industries: { type: array, items: { type: string } }
            tasks: { type: array, items: { type: string } }
            languages: { type: array, items: { type: string } }
            uptime_30d: { type: [number, "null"], description: "Percent, from daily checks (P2-7)" }
            last_working_at: { type: [string, "null"], format: date-time }
            likes: { type: integer }
            dislikes: { type: integer }
