openapi: 3.0.3
info:
  title: SEO Audit
  description: |
    Comprehensive SEO audit for any public web page. Analyzes title tags, meta descriptions,
    heading structure, images, mobile readiness, performance, social tags, and crawlability.
    Returns a score (0-100), grade (A-F), and detailed findings.

    **Authentication (optional):** Add an `X-API-Key` header for higher limits (5 req/min, 50/day vs anonymous 10/day cap).
    Get a free API key instantly at https://hermesforge.dev/tools/seo or POST to `/api/keys` with `{}` (anonymous, 20/day) or `{"email": "you@example.com"}` (50/day).

    Without a key: 5 requests per minute per IP. With a free key: 5 req/min, 50/day, 500/month.
  version: "1.0.0"
  contact:
    name: Hermes Agent
    email: hermes-agent@agentmail.to
    url: https://hermesforge.dev/

externalDocs:
  description: Try this tool in the browser
  url: https://hermesforge.dev/tools/seo

servers:
  - url: https://hermesforge.dev

paths:
  /api/seo:
    get:
      operationId: seoAudit
      summary: Run an SEO audit on a web page
      description: |
        Performs a comprehensive SEO analysis of the specified URL, checking title, meta tags,
        headings, images, mobile readiness, page speed, social tags, and crawlability signals.
        Rate limited to 1 request per 2 minutes per IP.
      parameters:
        - name: url
          in: query
          required: true
          description: The URL to audit. Must be a public URL.
          schema:
            type: string
            example: "https://example.com"
      responses:
        "200":
          description: Audit completed successfully.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SEOReport"
        "400":
          description: Invalid request.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Rate limit exceeded.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Audit failed.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

security:
  - ApiKeyAuth: []
  - {}

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: |
        Optional API key for higher rate limits. Get one free at
        https://hermesforge.dev/tools/seo or POST /api/keys with {} (anonymous, 20/day) or {"email": "you@example.com"} (50/day).
  schemas:
    SEOReport:
      type: object
      properties:
        url:
          type: string
          example: "https://example.com"
        score:
          type: integer
          minimum: 0
          maximum: 100
          description: Overall SEO score.
          example: 78
        grade:
          type: string
          enum: ["A", "B", "C", "D", "F"]
          description: Letter grade based on score (A>=90, B>=75, C>=60, D>=40, F<40).
          example: "B"
        issues:
          type: array
          description: Critical and high-severity problems found.
          items:
            $ref: "#/components/schemas/Issue"
        warnings:
          type: array
          description: Medium and low-severity suggestions.
          items:
            $ref: "#/components/schemas/Issue"
        passed:
          type: array
          description: Checks that passed successfully.
          items:
            $ref: "#/components/schemas/PassedCheck"
        summary:
          type: object
          properties:
            issues_count:
              type: integer
              example: 2
            warnings_count:
              type: integer
              example: 3
            passed_count:
              type: integer
              example: 10
        meta:
          type: object
          description: Page metadata extracted during the audit.
          properties:
            status_code:
              type: integer
              example: 200
            load_time_seconds:
              type: number
              format: float
              example: 1.23
            title:
              type: string
              example: "Example Domain"
            meta_description:
              type: string
              example: "This domain is for use in illustrative examples."
            word_count:
              type: integer
              example: 450
            heading_structure:
              type: object
              properties:
                h1:
                  type: integer
                h2:
                  type: integer
                h3:
                  type: integer
                h4:
                  type: integer
                h5:
                  type: integer
                h6:
                  type: integer
            links:
              type: object
              properties:
                total:
                  type: integer
                internal:
                  type: integer
                external:
                  type: integer
                nofollow:
                  type: integer
        heading_structure:
          type: object
          description: All headings found on the page, grouped by level.
          properties:
            h1:
              type: array
              items:
                type: string
            h2:
              type: array
              items:
                type: string
            h3:
              type: array
              items:
                type: string
            h4:
              type: array
              items:
                type: string
            h5:
              type: array
              items:
                type: string
            h6:
              type: array
              items:
                type: string
        timestamp:
          type: string
          format: date-time
          example: "2026-02-26T15:30:00Z"
    Issue:
      type: object
      properties:
        category:
          type: string
          description: The SEO category this issue belongs to.
          enum: ["title", "meta", "headings", "images", "mobile", "accessibility", "performance", "social", "crawlability"]
          example: "title"
        issue:
          type: string
          description: Human-readable description of the issue.
          example: "Title tag is too long (75 characters). Recommended: 30-60 characters."
        severity:
          type: string
          enum: ["critical", "high", "medium", "warning", "low"]
          example: "medium"
    PassedCheck:
      type: object
      properties:
        category:
          type: string
          example: "mobile"
        detail:
          type: string
          example: "Viewport meta tag is properly configured."
    Error:
      type: object
      properties:
        error:
          type: string
          example: "Rate limit exceeded. Max 1 audit per 2 minutes."
