{
  "openapi": "3.1.0",
  "info": {
    "title": "Shotpane API",
    "version": "1.0.0",
    "summary": "Screenshot, website-to-PDF, and OG-image rendering API.",
    "description": "Render any public web page to a screenshot (PNG/JPEG), an A4 PDF, or a 1200x630 OG/social image with a single GET request. Authenticate with an API key passed as a `?key=` query parameter or an `Authorization: Bearer <key>` header. Free tier: 100 renders/month, no card required (get a key at https://www.shotpane.com/#get-key). The same endpoints are exposed as an MCP server at https://www.shotpane.com/mcp for AI agents.",
    "contact": { "name": "Shotpane", "url": "https://www.shotpane.com" }
  },
  "servers": [{ "url": "https://www.shotpane.com", "description": "Production" }],
  "externalDocs": { "description": "Docs & MCP setup", "url": "https://www.shotpane.com/docs" },
  "tags": [
    { "name": "Render", "description": "URL-to-asset rendering endpoints." }
  ],
  "security": [{ "queryKey": [] }, { "bearerAuth": [] }],
  "paths": {
    "/api/v1/screenshot": {
      "get": {
        "operationId": "getScreenshot",
        "summary": "Capture a screenshot of a URL",
        "description": "Renders a public http(s) URL to a PNG or JPEG image. Supports full-page capture and a custom viewport. Internal, private, and cloud-metadata addresses are rejected.",
        "tags": ["Render"],
        "parameters": [
          { "$ref": "#/components/parameters/url" },
          { "$ref": "#/components/parameters/key" },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "description": "Image format. Defaults to png.",
            "schema": { "type": "string", "enum": ["png", "jpeg"], "default": "png" }
          },
          {
            "name": "full_page",
            "in": "query",
            "required": false,
            "description": "Capture the full scrollable page instead of just the viewport.",
            "schema": { "type": "boolean", "default": false }
          },
          {
            "name": "width",
            "in": "query",
            "required": false,
            "description": "Viewport width in pixels.",
            "schema": { "type": "integer", "minimum": 1, "default": 1280 }
          },
          {
            "name": "height",
            "in": "query",
            "required": false,
            "description": "Viewport height in pixels.",
            "schema": { "type": "integer", "minimum": 1, "default": 800 }
          }
        ],
        "responses": {
          "200": {
            "description": "The rendered image.",
            "content": {
              "image/png": { "schema": { "type": "string", "format": "binary" } },
              "image/jpeg": { "schema": { "type": "string", "format": "binary" } }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/ServerError" },
          "502": { "$ref": "#/components/responses/RenderFailed" }
        }
      }
    },
    "/api/v1/pdf": {
      "get": {
        "operationId": "getPdf",
        "summary": "Render a URL to a PDF",
        "description": "Renders a public http(s) URL to a print-ready A4 PDF with backgrounds.",
        "tags": ["Render"],
        "parameters": [
          { "$ref": "#/components/parameters/url" },
          { "$ref": "#/components/parameters/key" }
        ],
        "responses": {
          "200": {
            "description": "The rendered PDF.",
            "content": {
              "application/pdf": { "schema": { "type": "string", "format": "binary" } }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/ServerError" },
          "502": { "$ref": "#/components/responses/RenderFailed" }
        }
      }
    },
    "/api/v1/og": {
      "get": {
        "operationId": "getOgImage",
        "summary": "Render a URL as a 1200x630 OG image",
        "description": "Renders a public http(s) URL at the 1200x630 social-card size and returns a PNG. Point it at your own /og route to generate dynamic Open Graph images without bundling Chromium.",
        "tags": ["Render"],
        "parameters": [
          { "$ref": "#/components/parameters/url" },
          { "$ref": "#/components/parameters/key" }
        ],
        "responses": {
          "200": {
            "description": "The rendered 1200x630 PNG.",
            "content": {
              "image/png": { "schema": { "type": "string", "format": "binary" } }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/ServerError" },
          "502": { "$ref": "#/components/responses/RenderFailed" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "queryKey": {
        "type": "apiKey",
        "in": "query",
        "name": "key",
        "description": "API key passed as the `key` query parameter."
      },
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "API key passed as `Authorization: Bearer <key>`."
      }
    },
    "parameters": {
      "url": {
        "name": "url",
        "in": "query",
        "required": true,
        "description": "The public http(s) URL to render. Internal, private, and cloud-metadata addresses are rejected.",
        "schema": { "type": "string", "format": "uri" },
        "example": "https://stripe.com"
      },
      "key": {
        "name": "key",
        "in": "query",
        "required": false,
        "description": "Your API key. Required unless you send it via the Authorization: Bearer header.",
        "schema": { "type": "string" },
        "example": "sp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "description": "A short machine-readable reason (e.g. invalid_key, limit_reached, render_failed, or a validation message)."
          }
        },
        "required": ["error"]
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Missing url, invalid URL, or a blocked/internal target.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "Missing 'url' parameter" } } }
      },
      "Unauthorized": {
        "description": "Missing or invalid API key.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "invalid_key" } } }
      },
      "RateLimited": {
        "description": "Monthly render quota for this key has been reached.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "limit_reached" } } }
      },
      "ServerError": {
        "description": "Server is not configured or an unexpected error occurred.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "Server not configured" } } }
      },
      "RenderFailed": {
        "description": "The upstream render failed (target page errored or timed out).",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "render_failed" } } }
      }
    }
  }
}
