{
  "openapi": "3.1.0",
  "info": {
    "title": "Page analysis API",
    "version": "v1",
    "description": "Server-to-server API that analyzes one public web page for AI findability (22 metrics in 4 pillars) and returns a structured result. Analyses run asynchronously: submit a URL, then poll the analysis or receive a webhook. All errors are `application/problem+json` with a stable `code`. Unsupported methods on a known path return `405 method_not_allowed` with an `Allow` header; unknown paths return `404 not_found`. There is no CORS: call the API from your server only. Request bodies are limited to 16384 bytes."
  },
  "servers": [
    {
      "url": "https://api.geo-score.online/v1"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Analyses"
    },
    {
      "name": "Usage"
    },
    {
      "name": "Knowledge"
    }
  ],
  "paths": {
    "/analyses": {
      "post": {
        "operationId": "createAnalysis",
        "tags": [
          "Analyses"
        ],
        "summary": "Submit a URL for analysis",
        "description": "Test keys (`gs_test_…`) return a deterministic fixture result immediately (status `completed`), never fetch the URL and never use quota. Test and live keys are separate: each sees only analyses of its own mode and has its own rate-limit window. Live keys use one unit of the monthly quota per new analysis (202); it is not refunded if the analysis fails.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateAnalysisRequest"
              },
              "example": {
                "url": "https://shop.example.com/products/blue-ceramic-mug",
                "language": "en",
                "page_type": "product"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "An existing analysis was returned (Idempotency-Key replay, or the same normalized URL + language is still queued/running for your account). No quota is used.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Location": {
                "$ref": "#/components/headers/Location"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalysisSubmitted"
                },
                "examples": {
                  "existing": {
                    "summary": "Existing analysis",
                    "value": {
                      "id": "an_5c2e9a7f1b3d4e6a8c0f2b4d",
                      "status": "queued",
                      "url": "https://shop.example.com/products/blue-ceramic-mug",
                      "created_at": "2026-11-01T08:59:12.000Z",
                      "links": {
                        "self": "/v1/analyses/an_5c2e9a7f1b3d4e6a8c0f2b4d"
                      }
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "Accepted: a new analysis was created. Live keys: one unit of the monthly quota was used.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Location": {
                "$ref": "#/components/headers/Location"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalysisSubmitted"
                },
                "examples": {
                  "queued": {
                    "summary": "Queued (live key)",
                    "value": {
                      "id": "an_5c2e9a7f1b3d4e6a8c0f2b4d",
                      "status": "queued",
                      "url": "https://shop.example.com/products/blue-ceramic-mug",
                      "created_at": "2026-11-01T08:59:12.000Z",
                      "links": {
                        "self": "/v1/analyses/an_5c2e9a7f1b3d4e6a8c0f2b4d"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`: The request is invalid. See errors for details.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "invalid_request": {
                    "summary": "Invalid Request",
                    "value": {
                      "type": "/v1/errors/invalid_request",
                      "title": "Invalid Request",
                      "status": 400,
                      "detail": "The request is invalid. See errors for details.",
                      "code": "invalid_request",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5",
                      "errors": [
                        {
                          "field": "url",
                          "message": "Must be an absolute http or https URL."
                        },
                        {
                          "field": "language",
                          "message": "Must be one of: nl, en."
                        },
                        {
                          "field": "page_type",
                          "message": "Must be one of: product, category, cms."
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: A valid API key is required in the Authorization header (Bearer).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "unauthorized": {
                    "summary": "Unauthorized",
                    "value": {
                      "type": "/v1/errors/unauthorized",
                      "title": "Unauthorized",
                      "status": 401,
                      "detail": "A valid API key is required in the Authorization header (Bearer).",
                      "code": "unauthorized",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "`forbidden`: This API account is suspended.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "forbidden": {
                    "summary": "Forbidden",
                    "value": {
                      "type": "/v1/errors/forbidden",
                      "title": "Forbidden",
                      "status": 403,
                      "detail": "This API account is suspended.",
                      "code": "forbidden",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "`idempotency_conflict`: This Idempotency-Key was already used with a different request.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "idempotency_conflict": {
                    "summary": "Idempotency Key Conflict",
                    "value": {
                      "type": "/v1/errors/idempotency_conflict",
                      "title": "Idempotency Key Conflict",
                      "status": 409,
                      "detail": "This Idempotency-Key was already used with a different request.",
                      "code": "idempotency_conflict",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5"
                    }
                  }
                }
              }
            }
          },
          "413": {
            "description": "`payload_too_large`: The request body exceeds the maximum size.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "payload_too_large": {
                    "summary": "Payload Too Large",
                    "value": {
                      "type": "/v1/errors/payload_too_large",
                      "title": "Payload Too Large",
                      "status": 413,
                      "detail": "The request body exceeds the maximum size.",
                      "code": "payload_too_large",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5",
                      "max_bytes": 16384
                    }
                  }
                }
              }
            }
          },
          "415": {
            "description": "`unsupported_media_type`: The request body must be JSON (Content-Type: application/json).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "unsupported_media_type": {
                    "summary": "Unsupported Media Type",
                    "value": {
                      "type": "/v1/errors/unsupported_media_type",
                      "title": "Unsupported Media Type",
                      "status": 415,
                      "detail": "The request body must be JSON (Content-Type: application/json).",
                      "code": "unsupported_media_type",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "`target_not_allowed`: The URL does not point to a public website and cannot be analyzed. `target_unresolvable`: The host name of the URL does not resolve.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "target_not_allowed": {
                    "summary": "Target Not Allowed",
                    "value": {
                      "type": "/v1/errors/target_not_allowed",
                      "title": "Target Not Allowed",
                      "status": 422,
                      "detail": "The URL does not point to a public website and cannot be analyzed.",
                      "code": "target_not_allowed",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5"
                    }
                  },
                  "target_unresolvable": {
                    "summary": "Target Unresolvable",
                    "value": {
                      "type": "/v1/errors/target_unresolvable",
                      "title": "Target Unresolvable",
                      "status": 422,
                      "detail": "The host name of the URL does not resolve.",
                      "code": "target_unresolvable",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "`quota_exceeded`: The monthly analysis quota is used up. `rate_limited`: Too many requests. Retry after the indicated time.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "quota_exceeded": {
                    "summary": "Quota Exceeded",
                    "value": {
                      "type": "/v1/errors/quota_exceeded",
                      "title": "Quota Exceeded",
                      "status": 429,
                      "detail": "The monthly analysis quota is used up.",
                      "code": "quota_exceeded",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5",
                      "limit": 1000,
                      "reset_at": "2026-12-01T00:00:00.000Z"
                    }
                  },
                  "rate_limited": {
                    "summary": "Rate Limited",
                    "value": {
                      "type": "/v1/errors/rate_limited",
                      "title": "Rate Limited",
                      "status": 429,
                      "detail": "Too many requests. Retry after the indicated time.",
                      "code": "rate_limited",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "`internal_error`: An unexpected error occurred. Please retry later.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "internal_error": {
                    "summary": "Internal Error",
                    "value": {
                      "type": "/v1/errors/internal_error",
                      "title": "Internal Error",
                      "status": 500,
                      "detail": "An unexpected error occurred. Please retry later.",
                      "code": "internal_error",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listAnalyses",
        "tags": [
          "Analyses"
        ],
        "summary": "List your analyses, newest first",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of analyses (without results; fetch one analysis for its result).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalysisList"
                },
                "examples": {
                  "page": {
                    "summary": "First page",
                    "value": {
                      "data": [
                        {
                          "id": "an_5c2e9a7f1b3d4e6a8c0f2b4d",
                          "status": "completed",
                          "url": "https://shop.example.com/products/blue-ceramic-mug",
                          "language": "en",
                          "page_type_hint": "product",
                          "created_at": "2026-11-01T08:59:12.000Z",
                          "started_at": "2026-11-01T08:59:14.000Z",
                          "completed_at": "2026-11-01T09:00:00.000Z",
                          "error": null
                        },
                        {
                          "id": "an_9b1d3f5a7c9e1b3d5f7a9c1e",
                          "status": "failed",
                          "url": "https://shop.example.com/kitchen/mugs",
                          "language": "en",
                          "page_type_hint": "category",
                          "created_at": "2026-11-01T08:59:12.000Z",
                          "started_at": "2026-11-01T08:59:14.000Z",
                          "completed_at": "2026-11-01T09:03:40.000Z",
                          "error": {
                            "code": "target_unreachable"
                          }
                        }
                      ],
                      "next_cursor": "MjAyNi0xMS0wMVQwODo1OToxMi4wMDBafGFuXzljMWQzZjVhN2M5ZTFiM2Q1ZjdhOWMxZQ"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`: The request is invalid. See errors for details.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "invalid_request": {
                    "summary": "Invalid Request",
                    "value": {
                      "type": "/v1/errors/invalid_request",
                      "title": "Invalid Request",
                      "status": 400,
                      "detail": "The request is invalid. See errors for details.",
                      "code": "invalid_request",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5",
                      "errors": [
                        {
                          "field": "url",
                          "message": "Must be an absolute http or https URL."
                        },
                        {
                          "field": "language",
                          "message": "Must be one of: nl, en."
                        },
                        {
                          "field": "page_type",
                          "message": "Must be one of: product, category, cms."
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: A valid API key is required in the Authorization header (Bearer).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "unauthorized": {
                    "summary": "Unauthorized",
                    "value": {
                      "type": "/v1/errors/unauthorized",
                      "title": "Unauthorized",
                      "status": 401,
                      "detail": "A valid API key is required in the Authorization header (Bearer).",
                      "code": "unauthorized",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "`forbidden`: This API account is suspended.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "forbidden": {
                    "summary": "Forbidden",
                    "value": {
                      "type": "/v1/errors/forbidden",
                      "title": "Forbidden",
                      "status": 403,
                      "detail": "This API account is suspended.",
                      "code": "forbidden",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: Too many requests. Retry after the indicated time.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "rate_limited": {
                    "summary": "Rate Limited",
                    "value": {
                      "type": "/v1/errors/rate_limited",
                      "title": "Rate Limited",
                      "status": 429,
                      "detail": "Too many requests. Retry after the indicated time.",
                      "code": "rate_limited",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "`internal_error`: An unexpected error occurred. Please retry later.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "internal_error": {
                    "summary": "Internal Error",
                    "value": {
                      "type": "/v1/errors/internal_error",
                      "title": "Internal Error",
                      "status": 500,
                      "detail": "An unexpected error occurred. Please retry later.",
                      "code": "internal_error",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/analyses/{id}": {
      "get": {
        "operationId": "getAnalysis",
        "tags": [
          "Analyses"
        ],
        "summary": "Get an analysis (status and, when completed, the result)",
        "parameters": [
          {
            "$ref": "#/components/parameters/AnalysisIdPath"
          }
        ],
        "responses": {
          "200": {
            "description": "The analysis.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Analysis"
                },
                "examples": {
                  "queued": {
                    "summary": "Queued",
                    "value": {
                      "id": "an_5c2e9a7f1b3d4e6a8c0f2b4d",
                      "status": "queued",
                      "url": "https://shop.example.com/products/blue-ceramic-mug",
                      "language": "en",
                      "page_type_hint": "product",
                      "created_at": "2026-11-01T08:59:12.000Z",
                      "started_at": null,
                      "completed_at": null,
                      "error": null,
                      "result": null
                    }
                  },
                  "running": {
                    "summary": "Running",
                    "value": {
                      "id": "an_5c2e9a7f1b3d4e6a8c0f2b4d",
                      "status": "running",
                      "url": "https://shop.example.com/products/blue-ceramic-mug",
                      "language": "en",
                      "page_type_hint": "product",
                      "created_at": "2026-11-01T08:59:12.000Z",
                      "started_at": "2026-11-01T08:59:14.000Z",
                      "completed_at": null,
                      "error": null,
                      "result": null
                    }
                  },
                  "completed": {
                    "summary": "Completed",
                    "value": {
                      "id": "an_5c2e9a7f1b3d4e6a8c0f2b4d",
                      "status": "completed",
                      "url": "https://shop.example.com/products/blue-ceramic-mug",
                      "language": "en",
                      "page_type_hint": "product",
                      "created_at": "2026-11-01T08:59:12.000Z",
                      "started_at": "2026-11-01T08:59:14.000Z",
                      "completed_at": "2026-11-01T09:00:00.000Z",
                      "error": null,
                      "result": {
                        "url": "https://shop.example.com/products/blue-ceramic-mug",
                        "analyzed_at": "2026-11-01T09:00:00.000Z",
                        "api_version": "v1",
                        "methodology_version": "2026.11.0",
                        "page_type": "product",
                        "overall_score": 57,
                        "pillars": [
                          {
                            "id": "content_quality",
                            "name": "Content Quality",
                            "score": 52
                          },
                          {
                            "id": "ai_readiness",
                            "name": "AI Readiness",
                            "score": 57
                          },
                          {
                            "id": "technical_basis",
                            "name": "Technical Basis",
                            "score": 76
                          },
                          {
                            "id": "authority_trust",
                            "name": "Authority & Trust",
                            "score": 43
                          }
                        ],
                        "metrics": [
                          {
                            "id": "readability",
                            "name": "Readability",
                            "pillar": "content_quality",
                            "score": 68,
                            "status": "warn",
                            "explanation": "How easy the text is to read, based on sentence length, word complexity and paragraph structure.",
                            "recommendation": "Simplify the sentences that are still long and start or end each section with a clear summary sentence.",
                            "priority": "medium"
                          },
                          {
                            "id": "comprehensiveness",
                            "name": "Comprehensiveness",
                            "pillar": "content_quality",
                            "score": 41,
                            "status": "fail",
                            "explanation": "How deeply the page covers its topic: amount of content, sections, examples, definitions and media.",
                            "recommendation": "Cover the subtopics and related questions people have, organise them under clear headings, define key terms and add supporting images or tables.",
                            "priority": "high"
                          },
                          {
                            "id": "transition_words",
                            "name": "Transition Words",
                            "pillar": "content_quality",
                            "score": null,
                            "status": "not_applicable",
                            "explanation": "How well sentences and paragraphs are connected with transition words that make the logical flow explicit.",
                            "recommendation": null,
                            "priority": null
                          },
                          {
                            "id": "lsi_keywords",
                            "name": "LSI Keywords",
                            "pillar": "content_quality",
                            "score": 57,
                            "status": "warn",
                            "explanation": "How varied the related terms, synonyms and topic variations on the page are.",
                            "recommendation": "Add sections on related concepts and include question-based variations of the main keywords.",
                            "priority": "medium"
                          },
                          {
                            "id": "answer_completeness",
                            "name": "Answer Completeness",
                            "pillar": "content_quality",
                            "score": 38,
                            "status": "fail",
                            "explanation": "Whether the page gives complete, self-contained answers that AI engines can quote directly.",
                            "recommendation": "List the main questions the page should answer and give each one a paragraph that fully answers it without needing the rest of the page.",
                            "priority": "high"
                          },
                          {
                            "id": "factual_density",
                            "name": "Factual Density",
                            "pillar": "content_quality",
                            "score": 72,
                            "status": "warn",
                            "explanation": "How many verifiable facts the text contains: figures, dates, named sources and concrete data points.",
                            "recommendation": "Give every section at least one concrete data point or verifiable claim and replace outdated figures.",
                            "priority": "medium"
                          },
                          {
                            "id": "citability",
                            "name": "Citability",
                            "pillar": "content_quality",
                            "score": null,
                            "status": "not_applicable",
                            "explanation": "How easily AI engines can lift and quote individual text blocks: self-contained, factual and free of promotional language.",
                            "recommendation": null,
                            "priority": null
                          },
                          {
                            "id": "ai_optimization",
                            "name": "AI Optimization",
                            "pillar": "ai_readiness",
                            "score": 46,
                            "status": "fail",
                            "explanation": "Whether the page uses formats AI engines extract easily: questions and answers, definitions, summaries, lists and structured data.",
                            "recommendation": "Add a visible FAQ section with short direct answers, phrase headings as questions, and define key terms in an \"X is ...\" format.",
                            "priority": "high"
                          },
                          {
                            "id": "structure",
                            "name": "Content Structure",
                            "pillar": "ai_readiness",
                            "score": 62,
                            "status": "warn",
                            "explanation": "The heading hierarchy, structured data, lists, tables and other structural elements of the page.",
                            "recommendation": "Add a clearly headed FAQ section and, for long pages, a table of contents.",
                            "priority": "medium"
                          },
                          {
                            "id": "image_alt_text",
                            "name": "Image Alt-Text",
                            "pillar": "ai_readiness",
                            "score": 58,
                            "status": "warn",
                            "explanation": "How many images have alt text and how descriptive that alt text is.",
                            "recommendation": "Some images have no alt text. Add a short description of what each of these images shows.",
                            "priority": "medium"
                          },
                          {
                            "id": "platform_specificity",
                            "name": "Platform Specificity",
                            "pillar": "ai_readiness",
                            "score": 52,
                            "status": "warn",
                            "explanation": "How well the page matches the signals the major AI answer platforms each look for.",
                            "recommendation": "Format key content for direct answers: a definition paragraph, a numbered list or a comparison table.",
                            "priority": "medium"
                          },
                          {
                            "id": "content_type_matching",
                            "name": "Content Type Matching",
                            "pillar": "ai_readiness",
                            "score": 80,
                            "status": "pass",
                            "explanation": "Whether the structure of the page matches what is expected for its content type, such as steps for a how-to or a table for a comparison.",
                            "recommendation": "Make sure every section follows the expected pattern for the content type.",
                            "priority": "low"
                          },
                          {
                            "id": "semantic_clarity",
                            "name": "Semantic Clarity",
                            "pillar": "ai_readiness",
                            "score": 77,
                            "status": "pass",
                            "explanation": "How unambiguous the language is: vague words, unexplained jargon, unclear references and passive voice.",
                            "recommendation": "Check that each paragraph can be understood by someone without prior knowledge.",
                            "priority": "low"
                          },
                          {
                            "id": "ai_bot_crawler",
                            "name": "AI Bot Access",
                            "pillar": "technical_basis",
                            "score": 100,
                            "status": "pass",
                            "explanation": "Whether robots.txt allows the AI crawlers that collect content for AI answers.",
                            "recommendation": "Check robots.txt after changes so no new blocks for AI crawlers are added.",
                            "priority": "low"
                          },
                          {
                            "id": "schema_validator",
                            "name": "Schema Validator",
                            "pillar": "technical_basis",
                            "score": 84,
                            "status": "pass",
                            "explanation": "Whether the JSON-LD structured data on the page is present, valid and complete.",
                            "recommendation": "Add nested details such as author information and mentioned entities.",
                            "priority": "low"
                          },
                          {
                            "id": "sitemap_discoverability",
                            "name": "Sitemap & Discoverability",
                            "pillar": "technical_basis",
                            "score": 70,
                            "status": "warn",
                            "explanation": "Whether the site has an XML sitemap, whether this page is in it, and how well the page is linked internally.",
                            "recommendation": "This page is not listed in the XML sitemap. Add it so crawlers can find it.",
                            "priority": "medium"
                          },
                          {
                            "id": "page_speed",
                            "name": "Page Speed & Renderability",
                            "pillar": "technical_basis",
                            "score": 61,
                            "status": "warn",
                            "explanation": "How fast the page loads and whether its content is available without running JavaScript.",
                            "recommendation": "Inline critical CSS, defer the rest, and split or lazy-load non-critical JavaScript.",
                            "priority": "medium"
                          },
                          {
                            "id": "freshness",
                            "name": "Content Freshness",
                            "pillar": "technical_basis",
                            "score": 30,
                            "status": "fail",
                            "explanation": "How recent the content is, based on publication and update dates on the page and in structured data.",
                            "recommendation": "No publication or update date was found. Show the dates on the page and add datePublished and dateModified to the structured data.",
                            "priority": "high"
                          },
                          {
                            "id": "citations",
                            "name": "Citations & Sources",
                            "pillar": "authority_trust",
                            "score": 22,
                            "status": "fail",
                            "explanation": "Whether the page refers to authoritative external sources with links, quotes or a reference section.",
                            "recommendation": "Link to authoritative external sources such as official bodies, research or major industry publications, and quote experts with attribution.",
                            "priority": "high"
                          },
                          {
                            "id": "eeat",
                            "name": "E-E-A-T Signals",
                            "pillar": "authority_trust",
                            "score": 48,
                            "status": "fail",
                            "explanation": "Signals of experience, expertise, authority and trust: author information, credentials, about and contact pages.",
                            "recommendation": "No author information was found. Add an author box with name, role and expertise, ideally with Person structured data.",
                            "priority": "high"
                          },
                          {
                            "id": "knowledge_graph",
                            "name": "Knowledge Graph Readiness",
                            "pillar": "authority_trust",
                            "score": 66,
                            "status": "warn",
                            "explanation": "How clearly the page names entities (people, organizations, products, places) and their relationships.",
                            "recommendation": "Refer to specific people, organizations and products by name and use sameAs in structured data to link them to their official profiles.",
                            "priority": "medium"
                          },
                          {
                            "id": "topical_authority",
                            "name": "Topical Authority",
                            "pillar": "authority_trust",
                            "score": null,
                            "status": "not_applicable",
                            "explanation": "How well the page is embedded in a cluster of related content through internal links, related sections and breadcrumbs.",
                            "recommendation": null,
                            "priority": null
                          }
                        ],
                        "top_recommendations": [
                          {
                            "metric_id": "citations",
                            "title": "Cite authoritative sources",
                            "recommendation": "Link to authoritative external sources such as official bodies, research or major industry publications, and quote experts with attribution.",
                            "priority": "high"
                          },
                          {
                            "metric_id": "ai_optimization",
                            "title": "Add questions, answers and definitions",
                            "recommendation": "Add a visible FAQ section with short direct answers, phrase headings as questions, and define key terms in an \"X is ...\" format.",
                            "priority": "high"
                          },
                          {
                            "metric_id": "eeat",
                            "title": "Add author information",
                            "recommendation": "No author information was found. Add an author box with name, role and expertise, ideally with Person structured data.",
                            "priority": "high"
                          },
                          {
                            "metric_id": "comprehensiveness",
                            "title": "Add depth to the content",
                            "recommendation": "Cover the subtopics and related questions people have, organise them under clear headings, define key terms and add supporting images or tables.",
                            "priority": "high"
                          },
                          {
                            "metric_id": "structure",
                            "title": "Improve the structure",
                            "recommendation": "Add a clearly headed FAQ section and, for long pages, a table of contents.",
                            "priority": "medium"
                          }
                        ]
                      }
                    }
                  },
                  "failed": {
                    "summary": "Failed",
                    "value": {
                      "id": "an_9b1d3f5a7c9e1b3d5f7a9c1e",
                      "status": "failed",
                      "url": "https://shop.example.com/kitchen/mugs",
                      "language": "en",
                      "page_type_hint": "category",
                      "created_at": "2026-11-01T08:59:12.000Z",
                      "started_at": "2026-11-01T08:59:14.000Z",
                      "completed_at": "2026-11-01T09:03:40.000Z",
                      "error": {
                        "code": "target_unreachable"
                      },
                      "result": null
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: A valid API key is required in the Authorization header (Bearer).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "unauthorized": {
                    "summary": "Unauthorized",
                    "value": {
                      "type": "/v1/errors/unauthorized",
                      "title": "Unauthorized",
                      "status": 401,
                      "detail": "A valid API key is required in the Authorization header (Bearer).",
                      "code": "unauthorized",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "`forbidden`: This API account is suspended.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "forbidden": {
                    "summary": "Forbidden",
                    "value": {
                      "type": "/v1/errors/forbidden",
                      "title": "Forbidden",
                      "status": 403,
                      "detail": "This API account is suspended.",
                      "code": "forbidden",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`not_found`: The requested resource does not exist.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "not_found": {
                    "summary": "Not Found",
                    "value": {
                      "type": "/v1/errors/not_found",
                      "title": "Not Found",
                      "status": 404,
                      "detail": "The requested resource does not exist.",
                      "code": "not_found",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: Too many requests. Retry after the indicated time.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "rate_limited": {
                    "summary": "Rate Limited",
                    "value": {
                      "type": "/v1/errors/rate_limited",
                      "title": "Rate Limited",
                      "status": 429,
                      "detail": "Too many requests. Retry after the indicated time.",
                      "code": "rate_limited",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "`internal_error`: An unexpected error occurred. Please retry later.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "internal_error": {
                    "summary": "Internal Error",
                    "value": {
                      "type": "/v1/errors/internal_error",
                      "title": "Internal Error",
                      "status": 500,
                      "detail": "An unexpected error occurred. Please retry later.",
                      "code": "internal_error",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/usage": {
      "get": {
        "operationId": "getUsage",
        "tags": [
          "Usage"
        ],
        "summary": "Live quota usage in the current month",
        "responses": {
          "200": {
            "description": "Usage for the current calendar month (UTC).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Usage"
                },
                "examples": {
                  "usage": {
                    "summary": "Example values",
                    "value": {
                      "period": "2026-11",
                      "mode_counts": {
                        "live": {
                          "used": 120,
                          "limit": 1000,
                          "remaining": 880
                        }
                      },
                      "rate_limit_per_minute": 60,
                      "reset_at": "2026-12-01T00:00:00.000Z"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: A valid API key is required in the Authorization header (Bearer).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "unauthorized": {
                    "summary": "Unauthorized",
                    "value": {
                      "type": "/v1/errors/unauthorized",
                      "title": "Unauthorized",
                      "status": 401,
                      "detail": "A valid API key is required in the Authorization header (Bearer).",
                      "code": "unauthorized",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "`forbidden`: This API account is suspended.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "forbidden": {
                    "summary": "Forbidden",
                    "value": {
                      "type": "/v1/errors/forbidden",
                      "title": "Forbidden",
                      "status": 403,
                      "detail": "This API account is suspended.",
                      "code": "forbidden",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: Too many requests. Retry after the indicated time.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "rate_limited": {
                    "summary": "Rate Limited",
                    "value": {
                      "type": "/v1/errors/rate_limited",
                      "title": "Rate Limited",
                      "status": 429,
                      "detail": "Too many requests. Retry after the indicated time.",
                      "code": "rate_limited",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "`internal_error`: An unexpected error occurred. Please retry later.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "internal_error": {
                    "summary": "Internal Error",
                    "value": {
                      "type": "/v1/errors/internal_error",
                      "title": "Internal Error",
                      "status": 500,
                      "detail": "An unexpected error occurred. Please retry later.",
                      "code": "internal_error",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/knowledge": {
      "get": {
        "operationId": "getKnowledge",
        "tags": [
          "Knowledge"
        ],
        "summary": "What every metric measures, why it matters and how to improve it",
        "description": "Static content for your own UI (tooltips, help pages, fix guides): all pillars and all 22 metrics with explanation, quick wins and step-by-step improvement plans per score range. Contains no third-party branding and no links to us. Works with test and live keys, is rate limited like every endpoint and never uses quota. The content only changes with `methodology_version`, so cache it per language and methodology version.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Language"
          }
        ],
        "responses": {
          "200": {
            "description": "The knowledge base (example trimmed to one metric; the real response has all 22).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KnowledgeBase"
                },
                "examples": {
                  "knowledge": {
                    "summary": "English",
                    "value": {
                      "api_version": "v1",
                      "methodology_version": "2026.11.0",
                      "language": "en",
                      "status_bands": {
                        "pass_min": 75,
                        "warn_min": 50
                      },
                      "pillars": [
                        {
                          "id": "content_quality",
                          "name": "Content Quality",
                          "metric_ids": [
                            "readability",
                            "comprehensiveness",
                            "transition_words",
                            "lsi_keywords",
                            "answer_completeness",
                            "factual_density",
                            "citability"
                          ]
                        },
                        {
                          "id": "ai_readiness",
                          "name": "AI Readiness",
                          "metric_ids": [
                            "ai_optimization",
                            "structure",
                            "image_alt_text",
                            "platform_specificity",
                            "content_type_matching",
                            "semantic_clarity"
                          ]
                        },
                        {
                          "id": "technical_basis",
                          "name": "Technical Basis",
                          "metric_ids": [
                            "ai_bot_crawler",
                            "schema_validator",
                            "sitemap_discoverability",
                            "page_speed",
                            "freshness"
                          ]
                        },
                        {
                          "id": "authority_trust",
                          "name": "Authority & Trust",
                          "metric_ids": [
                            "citations",
                            "eeat",
                            "knowledge_graph",
                            "topical_authority"
                          ]
                        }
                      ],
                      "metrics": [
                        {
                          "id": "schema_validator",
                          "name": "Schema Validator",
                          "pillar": "technical_basis",
                          "explanation": "Whether the JSON-LD structured data on the page is present, valid and complete.",
                          "why_it_matters": "Valid, complete schema markup helps AI engines understand your content entities.",
                          "quick_wins": [
                            {
                              "title": "Add Article schema",
                              "text": "Add basic Article JSON-LD with headline, author, dates, and description.",
                              "impact": "high"
                            },
                            {
                              "title": "Fix validation errors",
                              "text": "Test your schema at schema.org validator and fix any errors.",
                              "impact": "medium"
                            }
                          ],
                          "improvement_steps": [
                            {
                              "min_score": 0,
                              "max_score": 40,
                              "steps": [
                                {
                                  "title": "Add primary schema",
                                  "text": "Add the most relevant schema type for your page as JSON-LD in the <head>.",
                                  "difficulty": "medium",
                                  "code": "<script type=\"application/ld+json\">\n{\n  \"@context\": \"https://schema.org\",\n  \"@type\": \"Article\",\n  \"headline\": \"How to Optimize for AI Search Engines\",\n  \"description\": \"Complete guide to GEO optimization\",\n  \"author\": {\n    \"@type\": \"Person\",\n    \"name\": \"Your Name\",\n    \"url\": \"https://yoursite.com/about\"\n  },\n  \"datePublished\": \"2026-01-15\",\n  \"dateModified\": \"2026-04-14\",\n  \"publisher\": {\n    \"@type\": \"Organization\",\n    \"name\": \"Your Site\",\n    \"logo\": { \"@type\": \"ImageObject\", \"url\": \"https://yoursite.com/logo.png\" }\n  }\n}\n</script>"
                                },
                                {
                                  "title": "Fill all required properties",
                                  "text": "Fill in the properties Google lists for your type (for Article: headline, image, author, datePublished, dateModified).",
                                  "difficulty": "easy",
                                  "code": null
                                },
                                {
                                  "title": "Validate with testing tool",
                                  "text": "Test at https://validator.schema.org/ and fix any warnings.",
                                  "difficulty": "easy",
                                  "code": null
                                }
                              ]
                            },
                            {
                              "min_score": 41,
                              "max_score": 70,
                              "steps": [
                                {
                                  "title": "Add additional schemas",
                                  "text": "Add BreadcrumbList, Organization or other types that match what is on the page.",
                                  "difficulty": "medium",
                                  "code": null
                                },
                                {
                                  "title": "Complete optional properties",
                                  "text": "Add image, wordCount, keywords, and other recommended properties.",
                                  "difficulty": "easy",
                                  "code": null
                                }
                              ]
                            },
                            {
                              "min_score": 71,
                              "max_score": 100,
                              "steps": [
                                {
                                  "title": "Add nested entities",
                                  "text": "Enrich schema with nested objects (author details, citations, mentions).",
                                  "difficulty": "medium",
                                  "code": null
                                }
                              ]
                            }
                          ]
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`: The request is invalid. See errors for details.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "invalid_request": {
                    "summary": "Invalid Request",
                    "value": {
                      "type": "/v1/errors/invalid_request",
                      "title": "Invalid Request",
                      "status": 400,
                      "detail": "The request is invalid. See errors for details.",
                      "code": "invalid_request",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5",
                      "errors": [
                        {
                          "field": "url",
                          "message": "Must be an absolute http or https URL."
                        },
                        {
                          "field": "language",
                          "message": "Must be one of: nl, en."
                        },
                        {
                          "field": "page_type",
                          "message": "Must be one of: product, category, cms."
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: A valid API key is required in the Authorization header (Bearer).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "unauthorized": {
                    "summary": "Unauthorized",
                    "value": {
                      "type": "/v1/errors/unauthorized",
                      "title": "Unauthorized",
                      "status": 401,
                      "detail": "A valid API key is required in the Authorization header (Bearer).",
                      "code": "unauthorized",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "`forbidden`: This API account is suspended.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "forbidden": {
                    "summary": "Forbidden",
                    "value": {
                      "type": "/v1/errors/forbidden",
                      "title": "Forbidden",
                      "status": 403,
                      "detail": "This API account is suspended.",
                      "code": "forbidden",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: Too many requests. Retry after the indicated time.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "rate_limited": {
                    "summary": "Rate Limited",
                    "value": {
                      "type": "/v1/errors/rate_limited",
                      "title": "Rate Limited",
                      "status": 429,
                      "detail": "Too many requests. Retry after the indicated time.",
                      "code": "rate_limited",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "`internal_error`: An unexpected error occurred. Please retry later.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "internal_error": {
                    "summary": "Internal Error",
                    "value": {
                      "type": "/v1/errors/internal_error",
                      "title": "Internal Error",
                      "status": 500,
                      "detail": "An unexpected error occurred. Please retry later.",
                      "code": "internal_error",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/knowledge/metrics/{id}": {
      "get": {
        "operationId": "getMetricKnowledge",
        "tags": [
          "Knowledge"
        ],
        "summary": "Knowledge for one metric",
        "parameters": [
          {
            "$ref": "#/components/parameters/MetricIdPath"
          },
          {
            "$ref": "#/components/parameters/Language"
          }
        ],
        "responses": {
          "200": {
            "description": "One metric.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MetricKnowledgeResponse"
                },
                "examples": {
                  "metric": {
                    "summary": "schema_validator (English)",
                    "value": {
                      "api_version": "v1",
                      "methodology_version": "2026.11.0",
                      "language": "en",
                      "metric": {
                        "id": "schema_validator",
                        "name": "Schema Validator",
                        "pillar": "technical_basis",
                        "explanation": "Whether the JSON-LD structured data on the page is present, valid and complete.",
                        "why_it_matters": "Valid, complete schema markup helps AI engines understand your content entities.",
                        "quick_wins": [
                          {
                            "title": "Add Article schema",
                            "text": "Add basic Article JSON-LD with headline, author, dates, and description.",
                            "impact": "high"
                          },
                          {
                            "title": "Fix validation errors",
                            "text": "Test your schema at schema.org validator and fix any errors.",
                            "impact": "medium"
                          }
                        ],
                        "improvement_steps": [
                          {
                            "min_score": 0,
                            "max_score": 40,
                            "steps": [
                              {
                                "title": "Add primary schema",
                                "text": "Add the most relevant schema type for your page as JSON-LD in the <head>.",
                                "difficulty": "medium",
                                "code": "<script type=\"application/ld+json\">\n{\n  \"@context\": \"https://schema.org\",\n  \"@type\": \"Article\",\n  \"headline\": \"How to Optimize for AI Search Engines\",\n  \"description\": \"Complete guide to GEO optimization\",\n  \"author\": {\n    \"@type\": \"Person\",\n    \"name\": \"Your Name\",\n    \"url\": \"https://yoursite.com/about\"\n  },\n  \"datePublished\": \"2026-01-15\",\n  \"dateModified\": \"2026-04-14\",\n  \"publisher\": {\n    \"@type\": \"Organization\",\n    \"name\": \"Your Site\",\n    \"logo\": { \"@type\": \"ImageObject\", \"url\": \"https://yoursite.com/logo.png\" }\n  }\n}\n</script>"
                              },
                              {
                                "title": "Fill all required properties",
                                "text": "Fill in the properties Google lists for your type (for Article: headline, image, author, datePublished, dateModified).",
                                "difficulty": "easy",
                                "code": null
                              },
                              {
                                "title": "Validate with testing tool",
                                "text": "Test at https://validator.schema.org/ and fix any warnings.",
                                "difficulty": "easy",
                                "code": null
                              }
                            ]
                          },
                          {
                            "min_score": 41,
                            "max_score": 70,
                            "steps": [
                              {
                                "title": "Add additional schemas",
                                "text": "Add BreadcrumbList, Organization or other types that match what is on the page.",
                                "difficulty": "medium",
                                "code": null
                              },
                              {
                                "title": "Complete optional properties",
                                "text": "Add image, wordCount, keywords, and other recommended properties.",
                                "difficulty": "easy",
                                "code": null
                              }
                            ]
                          },
                          {
                            "min_score": 71,
                            "max_score": 100,
                            "steps": [
                              {
                                "title": "Add nested entities",
                                "text": "Enrich schema with nested objects (author details, citations, mentions).",
                                "difficulty": "medium",
                                "code": null
                              }
                            ]
                          }
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`: The request is invalid. See errors for details.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "invalid_request": {
                    "summary": "Invalid Request",
                    "value": {
                      "type": "/v1/errors/invalid_request",
                      "title": "Invalid Request",
                      "status": 400,
                      "detail": "The request is invalid. See errors for details.",
                      "code": "invalid_request",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5",
                      "errors": [
                        {
                          "field": "url",
                          "message": "Must be an absolute http or https URL."
                        },
                        {
                          "field": "language",
                          "message": "Must be one of: nl, en."
                        },
                        {
                          "field": "page_type",
                          "message": "Must be one of: product, category, cms."
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`unauthorized`: A valid API key is required in the Authorization header (Bearer).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWW-Authenticate"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "unauthorized": {
                    "summary": "Unauthorized",
                    "value": {
                      "type": "/v1/errors/unauthorized",
                      "title": "Unauthorized",
                      "status": 401,
                      "detail": "A valid API key is required in the Authorization header (Bearer).",
                      "code": "unauthorized",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "`forbidden`: This API account is suspended.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "forbidden": {
                    "summary": "Forbidden",
                    "value": {
                      "type": "/v1/errors/forbidden",
                      "title": "Forbidden",
                      "status": 403,
                      "detail": "This API account is suspended.",
                      "code": "forbidden",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`not_found`: The requested resource does not exist.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "not_found": {
                    "summary": "Not Found",
                    "value": {
                      "type": "/v1/errors/not_found",
                      "title": "Not Found",
                      "status": 404,
                      "detail": "The requested resource does not exist.",
                      "code": "not_found",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "`rate_limited`: Too many requests. Retry after the indicated time.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "rate_limited": {
                    "summary": "Rate Limited",
                    "value": {
                      "type": "/v1/errors/rate_limited",
                      "title": "Rate Limited",
                      "status": 429,
                      "detail": "Too many requests. Retry after the indicated time.",
                      "code": "rate_limited",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "`internal_error`: An unexpected error occurred. Please retry later.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "internal_error": {
                    "summary": "Internal Error",
                    "value": {
                      "type": "/v1/errors/internal_error",
                      "title": "Internal Error",
                      "status": 500,
                      "detail": "An unexpected error occurred. Please retry later.",
                      "code": "internal_error",
                      "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "analysisCompleted": {
      "post": {
        "summary": "analysis.completed event",
        "description": "Sent as an HTTPS POST to your webhook URL. Verify `Webhook-Signature` before trusting the body: `t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\">`, keyed with your full webhook secret (`whsec_…`, as UTF-8). The header may carry several `v1` values (secret rotation): accept a match on any one and ignore unknown keys. Reject when |now - t| > 300 s. Any 2xx response marks the event delivered. Other responses, timeouts and redirects (never followed) are retried after 1m, 5m, 30m, 2h, 6h, 12h, 24h; 8 attempts in total. Delivery is at least once and not ordered: respond quickly and deduplicate on `Webhook-Id`. Sent for test-key analyses too (`data.mode` = `test`).",
        "parameters": [
          {
            "name": "Webhook-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Event id."
          },
          {
            "name": "Webhook-Timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unix seconds."
          },
          {
            "name": "Webhook-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^t=\\d+,v1=[0-9a-f]{64}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEvent"
              },
              "example": {
                "id": "evt_4a6c8e0b2d4f6a8c0e2b4d6f",
                "type": "analysis.completed",
                "created_at": "2026-11-01T09:00:00.000Z",
                "data": {
                  "analysis_id": "an_5c2e9a7f1b3d4e6a8c0f2b4d",
                  "mode": "live",
                  "url": "https://shop.example.com/products/blue-ceramic-mug",
                  "status": "completed",
                  "overall_score": 57
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Delivered."
          }
        }
      }
    },
    "analysisFailed": {
      "post": {
        "summary": "analysis.failed event",
        "description": "Sent as an HTTPS POST to your webhook URL. Verify `Webhook-Signature` before trusting the body: `t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\">`, keyed with your full webhook secret (`whsec_…`, as UTF-8). The header may carry several `v1` values (secret rotation): accept a match on any one and ignore unknown keys. Reject when |now - t| > 300 s. Any 2xx response marks the event delivered. Other responses, timeouts and redirects (never followed) are retried after 1m, 5m, 30m, 2h, 6h, 12h, 24h; 8 attempts in total. Delivery is at least once and not ordered: respond quickly and deduplicate on `Webhook-Id`. Sent for test-key analyses too (`data.mode` = `test`).",
        "parameters": [
          {
            "name": "Webhook-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Event id."
          },
          {
            "name": "Webhook-Timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unix seconds."
          },
          {
            "name": "Webhook-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^t=\\d+,v1=[0-9a-f]{64}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEvent"
              },
              "example": {
                "id": "evt_7e9a1c3e5a7c9e1b3d5f7a9c",
                "type": "analysis.failed",
                "created_at": "2026-11-01T09:03:40.000Z",
                "data": {
                  "analysis_id": "an_9b1d3f5a7c9e1b3d5f7a9c1e",
                  "mode": "live",
                  "url": "https://shop.example.com/kitchen/mugs",
                  "status": "failed",
                  "error_code": "target_unreachable"
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Delivered."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "`Authorization: Bearer gs_live_…` (live) or `gs_test_…` (test). Server-side only."
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Optional. Replaying the same key with the same request returns the original analysis (200) without using quota; the same key with a different URL, language or page type returns 409 `idempotency_conflict`. Keys are scoped per mode (test/live).",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 255,
          "pattern": "^[\\x21-\\x7e]+$"
        }
      },
      "AnalysisIdPath": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "$ref": "#/components/schemas/AnalysisId"
        }
      },
      "MetricIdPath": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Metric id as used in `metrics[].id` of an analysis result.",
        "schema": {
          "type": "string",
          "enum": [
            "readability",
            "comprehensiveness",
            "transition_words",
            "lsi_keywords",
            "answer_completeness",
            "factual_density",
            "citability",
            "ai_optimization",
            "structure",
            "image_alt_text",
            "platform_specificity",
            "content_type_matching",
            "semantic_clarity",
            "ai_bot_crawler",
            "schema_validator",
            "sitemap_discoverability",
            "page_speed",
            "freshness",
            "citations",
            "eeat",
            "knowledge_graph",
            "topical_authority"
          ]
        }
      },
      "Language": {
        "name": "language",
        "in": "query",
        "required": false,
        "schema": {
          "type": "string",
          "enum": [
            "nl",
            "en"
          ],
          "default": "en"
        }
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 20
        }
      },
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "required": false,
        "description": "`next_cursor` from the previous page.",
        "schema": {
          "type": "string",
          "maxLength": 200,
          "pattern": "^[A-Za-z0-9_-]+$"
        }
      }
    },
    "headers": {
      "X-Request-Id": {
        "description": "Unique id of this request (`req_…`). Include it when you contact support.",
        "schema": {
          "type": "string",
          "pattern": "^req_[0-9a-f]{32}$"
        }
      },
      "RateLimit-Limit": {
        "description": "Requests allowed in the current one-minute window.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "RateLimit-Remaining": {
        "description": "Requests left in the current window.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "RateLimit-Reset": {
        "description": "Seconds until the current window ends.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "Retry-After": {
        "description": "Seconds to wait before retrying.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      },
      "Location": {
        "description": "Path of the analysis resource.",
        "schema": {
          "type": "string"
        }
      },
      "WWW-Authenticate": {
        "description": "`Bearer` challenge.",
        "schema": {
          "type": "string"
        }
      }
    },
    "responses": {
      "method_not_allowed": {
        "description": "`method_not_allowed`: This method is not supported for this resource.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          },
          "Allow": {
            "description": "Supported methods.",
            "schema": {
              "type": "string"
            }
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "examples": {
              "method_not_allowed": {
                "summary": "Method Not Allowed",
                "value": {
                  "type": "/v1/errors/method_not_allowed",
                  "title": "Method Not Allowed",
                  "status": 405,
                  "detail": "This method is not supported for this resource.",
                  "code": "method_not_allowed",
                  "request_id": "req_6f1d2c3b4a5948e7b8c9d0e1f2a3b4c5"
                }
              }
            }
          }
        }
      }
    },
    "schemas": {
      "AnalysisId": {
        "type": "string",
        "pattern": "^an_[0-9a-f]{24}$",
        "examples": [
          "an_5c2e9a7f1b3d4e6a8c0f2b4d"
        ]
      },
      "AnalysisStatus": {
        "type": "string",
        "enum": [
          "queued",
          "running",
          "completed",
          "failed"
        ],
        "description": "`queued` and `running` are transient; `completed` and `failed` are final. A job that fails an attempt for a transient reason is re-queued and retried automatically before it is reported as `failed`."
      },
      "AnalysisError": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "code"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "Machine-readable reason. Known values: `target_not_allowed` (the URL, a redirect or a navigation of the page pointed at a non-public address; not retried); `target_unresolvable` (the host of the page, or of a redirect target, does not exist in DNS; not retried); `target_unreachable` (the page could not be loaded: connection refused or reset, TLS handshake failure, or a timeout; retried automatically first); `target_error` (the site answered, but the page cannot be analyzed: redirect loop, invalid response or an invalid or expired TLS certificate; not retried); `internal_error` (the analysis failed on our side; retried automatically first); `lease_expired` (the analysis was interrupted on our side on every attempt; treat it like internal_error; retried automatically first). New values may be added within v1; treat unknown values as a generic failure."
          }
        }
      },
      "CreateAnalysisRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "url"
        ],
        "properties": {
          "url": {
            "type": "string",
            "maxLength": 2048,
            "description": "Absolute http(s) URL of a publicly reachable page."
          },
          "language": {
            "type": "string",
            "enum": [
              "nl",
              "en"
            ],
            "default": "en",
            "description": "Language of the metric names, explanations and recommendations in the result."
          },
          "page_type": {
            "type": "string",
            "enum": [
              "product",
              "category",
              "cms"
            ],
            "description": "Optional hint for the kind of page (`cms` = content/CMS page). The detected type is returned as `result.page_type`."
          }
        }
      },
      "AnalysisSubmitted": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "status",
          "url",
          "created_at",
          "links"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/AnalysisId"
          },
          "status": {
            "$ref": "#/components/schemas/AnalysisStatus"
          },
          "url": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "self"
            ],
            "properties": {
              "self": {
                "type": "string",
                "description": "Path of the analysis resource."
              }
            }
          }
        }
      },
      "AnalysisSummary": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "status",
          "url",
          "language",
          "page_type_hint",
          "created_at",
          "started_at",
          "completed_at",
          "error"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/AnalysisId"
          },
          "status": {
            "$ref": "#/components/schemas/AnalysisStatus"
          },
          "url": {
            "type": "string",
            "description": "The URL as submitted."
          },
          "language": {
            "type": "string",
            "enum": [
              "nl",
              "en"
            ]
          },
          "page_type_hint": {
            "oneOf": [
              {
                "type": "string",
                "enum": [
                  "product",
                  "category",
                  "cms"
                ]
              },
              {
                "type": "null"
              }
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "started_at": {
            "oneOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ]
          },
          "completed_at": {
            "oneOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ]
          },
          "error": {
            "description": "Set only when status is `failed`.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/AnalysisError"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "Analysis": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "status",
          "url",
          "language",
          "page_type_hint",
          "created_at",
          "started_at",
          "completed_at",
          "error",
          "result"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/AnalysisId"
          },
          "status": {
            "$ref": "#/components/schemas/AnalysisStatus"
          },
          "url": {
            "type": "string",
            "description": "The URL as submitted."
          },
          "language": {
            "type": "string",
            "enum": [
              "nl",
              "en"
            ]
          },
          "page_type_hint": {
            "oneOf": [
              {
                "type": "string",
                "enum": [
                  "product",
                  "category",
                  "cms"
                ]
              },
              {
                "type": "null"
              }
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "started_at": {
            "oneOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ]
          },
          "completed_at": {
            "oneOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ]
          },
          "error": {
            "description": "Set only when status is `failed`.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/AnalysisError"
              },
              {
                "type": "null"
              }
            ]
          },
          "result": {
            "description": "Set only when status is `completed`.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/AnalysisResult"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "AnalysisList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AnalysisSummary"
            }
          },
          "next_cursor": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Pass as `cursor` to get the next page; `null` on the last page."
          }
        }
      },
      "Usage": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "period",
          "mode_counts",
          "rate_limit_per_minute",
          "reset_at"
        ],
        "properties": {
          "period": {
            "type": "string",
            "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])$",
            "description": "Calendar month (UTC)."
          },
          "mode_counts": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "live"
            ],
            "properties": {
              "live": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "used",
                  "limit",
                  "remaining"
                ],
                "properties": {
                  "used": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "limit": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "remaining": {
                    "type": "integer",
                    "minimum": 0
                  }
                }
              }
            },
            "description": "Only live analyses count. Test-key analyses are not counted."
          },
          "rate_limit_per_minute": {
            "type": "integer",
            "minimum": 0
          },
          "reset_at": {
            "type": "string",
            "format": "date-time",
            "description": "Start of the next calendar month (UTC), when the quota resets."
          }
        }
      },
      "MetricKnowledge": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "name",
          "pillar",
          "explanation",
          "why_it_matters",
          "quick_wins",
          "improvement_steps"
        ],
        "properties": {
          "id": {
            "type": "string",
            "enum": [
              "readability",
              "comprehensiveness",
              "transition_words",
              "lsi_keywords",
              "answer_completeness",
              "factual_density",
              "citability",
              "ai_optimization",
              "structure",
              "image_alt_text",
              "platform_specificity",
              "content_type_matching",
              "semantic_clarity",
              "ai_bot_crawler",
              "schema_validator",
              "sitemap_discoverability",
              "page_speed",
              "freshness",
              "citations",
              "eeat",
              "knowledge_graph",
              "topical_authority"
            ]
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "description": "Same as `metrics[].name` in an analysis result (same language)."
          },
          "pillar": {
            "type": "string",
            "enum": [
              "content_quality",
              "ai_readiness",
              "technical_basis",
              "authority_trust"
            ]
          },
          "explanation": {
            "type": "string",
            "minLength": 1,
            "description": "Same as `metrics[].explanation` in an analysis result."
          },
          "why_it_matters": {
            "type": "string",
            "minLength": 1
          },
          "quick_wins": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title",
                "text",
                "impact"
              ],
              "properties": {
                "title": {
                  "type": "string",
                  "minLength": 1
                },
                "text": {
                  "type": "string",
                  "minLength": 1
                },
                "impact": {
                  "type": "string",
                  "enum": [
                    "high",
                    "medium",
                    "low"
                  ]
                }
              }
            }
          },
          "improvement_steps": {
            "type": "array",
            "description": "Step-by-step plans per score range. Pick the range that contains the metric score of your analysis.",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "min_score",
                "max_score",
                "steps"
              ],
              "properties": {
                "min_score": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 100
                },
                "max_score": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 100
                },
                "steps": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "title",
                      "text",
                      "difficulty",
                      "code"
                    ],
                    "properties": {
                      "title": {
                        "type": "string",
                        "minLength": 1
                      },
                      "text": {
                        "type": "string",
                        "minLength": 1
                      },
                      "difficulty": {
                        "type": "string",
                        "enum": [
                          "easy",
                          "medium",
                          "hard"
                        ]
                      },
                      "code": {
                        "oneOf": [
                          {
                            "type": "string"
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "Example markup or configuration (not translated), or null."
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "KnowledgeBase": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "api_version",
          "methodology_version",
          "language",
          "status_bands",
          "pillars",
          "metrics"
        ],
        "properties": {
          "api_version": {
            "type": "string",
            "enum": [
              "v1"
            ]
          },
          "methodology_version": {
            "type": "string",
            "description": "Content can change when this changes; cache per value."
          },
          "language": {
            "type": "string",
            "enum": [
              "nl",
              "en"
            ]
          },
          "status_bands": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "pass_min",
              "warn_min"
            ],
            "properties": {
              "pass_min": {
                "type": "integer"
              },
              "warn_min": {
                "type": "integer"
              }
            },
            "description": "score >= pass_min is `pass`, score >= warn_min is `warn`, lower is `fail`."
          },
          "pillars": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "id",
                "name",
                "metric_ids"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "enum": [
                    "content_quality",
                    "ai_readiness",
                    "technical_basis",
                    "authority_trust"
                  ]
                },
                "name": {
                  "type": "string",
                  "minLength": 1
                },
                "metric_ids": {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "enum": [
                      "readability",
                      "comprehensiveness",
                      "transition_words",
                      "lsi_keywords",
                      "answer_completeness",
                      "factual_density",
                      "citability",
                      "ai_optimization",
                      "structure",
                      "image_alt_text",
                      "platform_specificity",
                      "content_type_matching",
                      "semantic_clarity",
                      "ai_bot_crawler",
                      "schema_validator",
                      "sitemap_discoverability",
                      "page_speed",
                      "freshness",
                      "citations",
                      "eeat",
                      "knowledge_graph",
                      "topical_authority"
                    ]
                  }
                }
              }
            }
          },
          "metrics": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MetricKnowledge"
            },
            "description": "All 22 metrics, in result order."
          }
        }
      },
      "MetricKnowledgeResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "api_version",
          "methodology_version",
          "language",
          "metric"
        ],
        "properties": {
          "api_version": {
            "type": "string",
            "enum": [
              "v1"
            ]
          },
          "methodology_version": {
            "type": "string"
          },
          "language": {
            "type": "string",
            "enum": [
              "nl",
              "en"
            ]
          },
          "metric": {
            "$ref": "#/components/schemas/MetricKnowledge"
          }
        }
      },
      "FieldError": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "field",
          "message"
        ],
        "properties": {
          "field": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details. Branch on `code`, not on `title` or `detail`.",
        "required": [
          "type",
          "title",
          "status",
          "detail",
          "code",
          "request_id"
        ],
        "properties": {
          "type": {
            "type": "string",
            "description": "Relative URI `/v1/errors/<code>`."
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "code": {
            "type": "string",
            "enum": [
              "unauthorized",
              "forbidden",
              "not_found",
              "method_not_allowed",
              "idempotency_conflict",
              "payload_too_large",
              "unsupported_media_type",
              "invalid_request",
              "target_not_allowed",
              "target_unresolvable",
              "quota_exceeded",
              "rate_limited",
              "internal_error"
            ]
          },
          "request_id": {
            "type": "string",
            "pattern": "^req_[0-9a-f]{32}$"
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FieldError"
            },
            "description": "`invalid_request` only."
          },
          "limit": {
            "type": "integer",
            "description": "`quota_exceeded` only: the monthly live quota."
          },
          "reset_at": {
            "type": "string",
            "format": "date-time",
            "description": "`quota_exceeded` only: when the quota resets."
          },
          "max_bytes": {
            "type": "integer",
            "description": "`payload_too_large` only."
          }
        }
      },
      "WebhookEvent": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "type",
          "created_at",
          "data"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Event id; also sent as the `Webhook-Id` header. Use it to deduplicate."
          },
          "type": {
            "type": "string",
            "enum": [
              "analysis.completed",
              "analysis.failed"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "data": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "analysis_id",
              "status",
              "mode",
              "url"
            ],
            "properties": {
              "analysis_id": {
                "$ref": "#/components/schemas/AnalysisId"
              },
              "status": {
                "type": "string",
                "enum": [
                  "completed",
                  "failed"
                ]
              },
              "mode": {
                "type": "string",
                "enum": [
                  "live",
                  "test"
                ],
                "description": "`test` for analyses created with a test key."
              },
              "url": {
                "type": "string",
                "description": "The URL as submitted."
              },
              "overall_score": {
                "type": "integer",
                "minimum": 0,
                "maximum": 100,
                "description": "`analysis.completed` only."
              },
              "error_code": {
                "type": "string",
                "description": "`analysis.failed` only. Same values as `error.code`."
              }
            }
          }
        }
      },
      "AnalysisResult": {
        "title": "AnalysisResultV1",
        "description": "Result of a single-page AI-findability analysis (API v1).",
        "type": "object",
        "additionalProperties": false,
        "required": [
          "url",
          "analyzed_at",
          "api_version",
          "methodology_version",
          "page_type",
          "overall_score",
          "pillars",
          "metrics",
          "top_recommendations"
        ],
        "properties": {
          "url": {
            "type": "string",
            "minLength": 1
          },
          "analyzed_at": {
            "type": "string",
            "format": "date-time"
          },
          "api_version": {
            "const": "v1"
          },
          "methodology_version": {
            "type": "string",
            "pattern": "^\\d{4}\\.\\d{2}\\.\\d+$"
          },
          "page_type": {
            "description": "Detected page type. New values MAY be added within v1; clients must tolerate unknown values.",
            "enum": [
              "blog-post",
              "listicle",
              "how-to",
              "comparison",
              "pillar-page",
              "faq",
              "case-study",
              "knowledge-base",
              "product",
              "service",
              "landing-page",
              "pricing",
              "category",
              "homepage",
              "about",
              "contact",
              "legal",
              "team",
              "portfolio"
            ]
          },
          "overall_score": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          },
          "pillars": {
            "type": "array",
            "minItems": 4,
            "maxItems": 4,
            "items": {
              "$ref": "#/components/schemas/ResultPillar"
            }
          },
          "metrics": {
            "type": "array",
            "minItems": 22,
            "maxItems": 22,
            "items": {
              "$ref": "#/components/schemas/ResultMetric"
            }
          },
          "top_recommendations": {
            "type": "array",
            "maxItems": 5,
            "items": {
              "$ref": "#/components/schemas/ResultTopRecommendation"
            }
          }
        }
      },
      "ResultPillarId": {
        "enum": [
          "content_quality",
          "ai_readiness",
          "technical_basis",
          "authority_trust"
        ]
      },
      "ResultMetricId": {
        "enum": [
          "readability",
          "comprehensiveness",
          "transition_words",
          "lsi_keywords",
          "answer_completeness",
          "factual_density",
          "citability",
          "ai_optimization",
          "structure",
          "image_alt_text",
          "platform_specificity",
          "content_type_matching",
          "semantic_clarity",
          "ai_bot_crawler",
          "schema_validator",
          "sitemap_discoverability",
          "page_speed",
          "freshness",
          "citations",
          "eeat",
          "knowledge_graph",
          "topical_authority"
        ]
      },
      "ResultPriority": {
        "enum": [
          "high",
          "medium",
          "low"
        ]
      },
      "ResultPillar": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "name",
          "score"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/ResultPillarId"
          },
          "name": {
            "type": "string",
            "minLength": 1
          },
          "score": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          }
        }
      },
      "ResultMetric": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "name",
          "pillar",
          "score",
          "status",
          "explanation",
          "recommendation",
          "priority"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/ResultMetricId"
          },
          "name": {
            "type": "string",
            "minLength": 1
          },
          "pillar": {
            "$ref": "#/components/schemas/ResultPillarId"
          },
          "score": {
            "oneOf": [
              {
                "type": "integer",
                "minimum": 0,
                "maximum": 100
              },
              {
                "type": "null"
              }
            ]
          },
          "status": {
            "enum": [
              "pass",
              "warn",
              "fail",
              "not_applicable"
            ]
          },
          "explanation": {
            "type": "string",
            "minLength": 1
          },
          "recommendation": {
            "oneOf": [
              {
                "type": "string",
                "minLength": 1
              },
              {
                "type": "null"
              }
            ]
          },
          "priority": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ResultPriority"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "ResultTopRecommendation": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "metric_id",
          "title",
          "recommendation",
          "priority"
        ],
        "properties": {
          "metric_id": {
            "$ref": "#/components/schemas/ResultMetricId"
          },
          "title": {
            "type": "string",
            "minLength": 1
          },
          "recommendation": {
            "type": "string",
            "minLength": 1
          },
          "priority": {
            "$ref": "#/components/schemas/ResultPriority"
          }
        }
      }
    }
  },
  "x-problem-types": {
    "unauthorized": {
      "type": "/v1/errors/unauthorized",
      "status": 401,
      "title": "Unauthorized"
    },
    "forbidden": {
      "type": "/v1/errors/forbidden",
      "status": 403,
      "title": "Forbidden"
    },
    "not_found": {
      "type": "/v1/errors/not_found",
      "status": 404,
      "title": "Not Found"
    },
    "method_not_allowed": {
      "type": "/v1/errors/method_not_allowed",
      "status": 405,
      "title": "Method Not Allowed"
    },
    "idempotency_conflict": {
      "type": "/v1/errors/idempotency_conflict",
      "status": 409,
      "title": "Idempotency Key Conflict"
    },
    "payload_too_large": {
      "type": "/v1/errors/payload_too_large",
      "status": 413,
      "title": "Payload Too Large"
    },
    "unsupported_media_type": {
      "type": "/v1/errors/unsupported_media_type",
      "status": 415,
      "title": "Unsupported Media Type"
    },
    "invalid_request": {
      "type": "/v1/errors/invalid_request",
      "status": 400,
      "title": "Invalid Request"
    },
    "target_not_allowed": {
      "type": "/v1/errors/target_not_allowed",
      "status": 422,
      "title": "Target Not Allowed"
    },
    "target_unresolvable": {
      "type": "/v1/errors/target_unresolvable",
      "status": 422,
      "title": "Target Unresolvable"
    },
    "quota_exceeded": {
      "type": "/v1/errors/quota_exceeded",
      "status": 429,
      "title": "Quota Exceeded"
    },
    "rate_limited": {
      "type": "/v1/errors/rate_limited",
      "status": 429,
      "title": "Rate Limited"
    },
    "internal_error": {
      "type": "/v1/errors/internal_error",
      "status": 500,
      "title": "Internal Error"
    }
  }
}
