{
  "openapi": "3.0.3",
  "info": {
    "title": "ScreenshotAPI",
    "version": "1.0.0",
    "description": "Render any URL to PNG / JPG / PDF / MP4 / GIF in milliseconds. Block ads, capture scrolling videos, embed with signed URLs. 24h file retention.",
    "contact": {
      "name": "ScreenshotAPI support",
      "url": "https://websitescreenshotapi.net/contact"
    },
    "license": { "name": "Proprietary", "url": "https://websitescreenshotapi.net/terms" }
  },
  "servers": [
    { "url": "https://websitescreenshotapi.net/api/v1", "description": "Production" }
  ],
  "security": [{ "bearerAuth": [] }],
  "paths": {
    "/screenshot": {
      "post": {
        "summary": "Render a single image / PDF (sync, ≤30s)",
        "tags": ["Screenshot"],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ScreenshotRequest" } } }
        },
        "responses": {
          "200": { "description": "Render done", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ScreenshotResponse" } } } },
          "202": { "description": "Render still processing", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProcessingResponse" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/QuotaExceeded" },
          "422": { "$ref": "#/components/responses/InvalidInput" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "502": { "$ref": "#/components/responses/TargetError" },
          "504": { "$ref": "#/components/responses/Timeout" }
        }
      }
    },
    "/screenshot-async": {
      "post": {
        "summary": "Submit a screenshot for async rendering",
        "tags": ["Screenshot"],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AsyncScreenshotRequest" } } }
        },
        "responses": {
          "202": { "description": "Job queued", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AsyncJobResponse" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/QuotaExceeded" },
          "422": { "$ref": "#/components/responses/InvalidInput" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/screenshot/{db_id}": {
      "get": {
        "summary": "Get the status / result of an async screenshot",
        "tags": ["Screenshot"],
        "parameters": [
          { "name": "db_id", "in": "path", "required": true, "schema": { "type": "integer" } }
        ],
        "responses": {
          "200": { "description": "Job state", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ScreenshotStatus" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/animate": {
      "post": {
        "summary": "Render an animation (MP4/WebM/GIF) — async by default",
        "tags": ["Animate"],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AnimateRequest" } } }
        },
        "responses": {
          "200": { "description": "Render done (sync mode)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AnimateResponse" } } } },
          "202": { "description": "Job queued (async)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AsyncJobResponse" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/QuotaExceeded" },
          "422": { "$ref": "#/components/responses/InvalidInput" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/bulk": {
      "post": {
        "summary": "Submit a batch of up to 500 URLs",
        "tags": ["Bulk"],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BulkRequest" } } }
        },
        "responses": {
          "202": { "description": "Batch queued", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BulkResponse" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/QuotaExceeded" },
          "422": { "$ref": "#/components/responses/InvalidInput" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/batch/{batch_id}": {
      "get": {
        "summary": "Get a batch status & results",
        "tags": ["Bulk"],
        "parameters": [
          { "name": "batch_id", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^bat_[a-f0-9]{32}$" } },
          { "name": "include",  "in": "query", "schema": { "type": "string", "enum": ["results"] }, "description": "Include all individual results" }
        ],
        "responses": {
          "200": { "description": "Batch state", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BatchStatus" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/render": {
      "get": {
        "summary": "Signed URL — embeddable directly in <img src> / <video src>",
        "tags": ["Embed"],
        "parameters": [
          { "name": "url",        "in": "query", "required": true, "schema": { "type": "string", "format": "uri" } },
          { "name": "format",     "in": "query", "schema": { "type": "string", "enum": ["png","jpg","webp","pdf"] } },
          { "name": "full_page",  "in": "query", "schema": { "type": "string", "enum": ["0","1"] } },
          { "name": "block_ads",  "in": "query", "schema": { "type": "string", "enum": ["0","1"] } },
          { "name": "viewport_width",  "in": "query", "schema": { "type": "integer", "minimum": 320, "maximum": 2560 } },
          { "name": "viewport_height", "in": "query", "schema": { "type": "integer", "minimum": 240, "maximum": 1440 } },
          { "name": "device",     "in": "query", "schema": { "type": "string" } },
          { "name": "selector",   "in": "query", "schema": { "type": "string" } },
          { "name": "delay",      "in": "query", "schema": { "type": "integer", "minimum": 0, "maximum": 10000 } },
          { "name": "wait_until", "in": "query", "schema": { "type": "string", "enum": ["load","domcontentloaded","networkidle","commit"] } },
          { "name": "access_key", "in": "query", "required": true, "schema": { "type": "string" }, "description": "Visible part of your API key (sho_<env>_<short>)" },
          { "name": "signature",  "in": "query", "required": true, "schema": { "type": "string" }, "description": "HMAC-SHA256 of canonical query string, signed with full API key as secret" }
        ],
        "responses": {
          "200": {
            "description": "Image binary",
            "headers": {
              "X-Cache":       { "schema": { "type": "string", "enum": ["HIT","MISS"] } },
              "Cache-Control": { "schema": { "type": "string" } }
            },
            "content": {
              "image/png":  {},
              "image/jpeg": {},
              "image/webp": {},
              "application/pdf": {}
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "description": "Invalid signature" },
          "410": { "description": "Result expired (24h retention)" },
          "422": { "$ref": "#/components/responses/InvalidInput" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/credits": {
      "get": {
        "summary": "Get current plan + quota",
        "tags": ["Account"],
        "responses": {
          "200": { "description": "Plan & quota", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreditsResponse" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/demo": {
      "post": {
        "summary": "Public demo (no auth, 3/IP/24h)",
        "tags": ["Public"],
        "security": [],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DemoRequest" } } }
        },
        "responses": {
          "200": { "description": "Render done", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ScreenshotResponse" } } } },
          "422": { "$ref": "#/components/responses/InvalidInput" },
          "429": { "description": "Demo limit reached" },
          "503": { "description": "Service at capacity" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "API key (sho_live_...)"
      }
    },
    "schemas": {
      "ScreenshotRequest": {
        "type": "object",
        "required": ["url"],
        "properties": {
          "url":                 { "type": "string", "format": "uri", "example": "https://example.com" },
          "format":              { "type": "string", "enum": ["png","jpg","jpeg","webp","pdf"], "default": "png" },
          "viewport_width":      { "type": "integer", "minimum": 320,  "maximum": 3840, "default": 1920 },
          "viewport_height":     { "type": "integer", "minimum": 240,  "maximum": 2400, "default": 1080 },
          "device_scale_factor": { "type": "number",  "minimum": 1.0,  "maximum": 3.0,  "default": 1 },
          "device":              { "type": "string", "description": "Preset: iphone_15_pro, pixel_7, ipad_pro_11, galaxy_s9, macbook_pro_16" },
          "full_page":           { "type": "boolean", "default": false },
          "selector":            { "type": "string", "description": "CSS selector to clip to" },
          "clip":                { "type": "object", "properties": { "x":{"type":"number"}, "y":{"type":"number"}, "width":{"type":"number"}, "height":{"type":"number"} } },
          "omit_background":     { "type": "boolean", "default": false },
          "quality":             { "type": "integer", "minimum": 20, "maximum": 100 },
          "pdf_paper":           { "type": "string", "enum": ["A4","Letter","Legal","Tabloid"] },
          "block_ads":           { "type": "boolean", "default": false },
          "block_cookie_banners":{ "type": "boolean", "default": false },
          "block_chat_widgets":  { "type": "boolean", "default": false },
          "dark_mode":           { "type": "boolean", "default": false },
          "reduced_motion":      { "type": "boolean", "default": false },
          "wait_until":          { "type": "string", "enum": ["load","domcontentloaded","networkidle","commit"], "default": "load" },
          "wait_for_selector":   { "type": "string" },
          "delay":               { "type": "integer", "minimum": 0, "maximum": 10000 },
          "user_agent":          { "type": "string" },
          "locale":              { "type": "string", "example": "en-US" },
          "basic_auth":          { "type": "string", "example": "user:pass" },
          "cookies":             { "type": "array",  "items": { "type": "object" } },
          "headers":             { "type": "object", "additionalProperties": { "type": "string" } }
        }
      },
      "AsyncScreenshotRequest": {
        "allOf": [
          { "$ref": "#/components/schemas/ScreenshotRequest" },
          {
            "type": "object",
            "properties": {
              "webhook_url":    { "type": "string", "format": "uri" },
              "webhook_secret": { "type": "string", "maxLength": 256 }
            }
          }
        ]
      },
      "ScreenshotResponse": {
        "type": "object",
        "properties": {
          "url":               { "type": "string", "format": "uri" },
          "expires_at":        { "type": "string", "format": "date-time" },
          "retention_hours":   { "type": "integer", "example": 24 },
          "format":            { "type": "string" },
          "size_bytes":        { "type": "integer" },
          "width":             { "type": "integer" },
          "height":            { "type": "integer" },
          "duration_ms":       { "type": "integer" },
          "credits_charged":   { "type": "integer" },
          "credits_remaining": { "type": "integer" },
          "plan_code":         { "type": "string" },
          "job_id":            { "type": "string" },
          "cache_hit":         { "type": "boolean" }
        }
      },
      "AnimateRequest": {
        "type": "object",
        "required": ["url"],
        "properties": {
          "url":                 { "type": "string", "format": "uri" },
          "format":              { "type": "string", "enum": ["mp4","webm","mov","avi","gif"], "default": "mp4" },
          "scenario":            { "type": "string", "enum": ["default","scroll"], "default": "default" },
          "duration":            { "type": "integer", "minimum": 1, "maximum": 30, "default": 5, "description": "Seconds (or max_duration if auto_duration)" },
          "auto_duration":       { "type": "boolean", "default": false, "description": "Adapt duration to page height" },
          "max_duration":        { "type": "integer", "minimum": 1, "maximum": 30, "description": "Cap when auto_duration=true (also billed)" },
          "scroll_speed_pps":    { "type": "integer", "minimum": 50, "maximum": 5000, "default": 400, "description": "Pixels per second of scroll (auto_duration mode)" },
          "scroll_mode":         { "type": "string", "enum": ["continuous","stepped"], "default": "continuous" },
          "scroll_easing":       { "type": "string", "default": "ease_in_out_quint" },
          "scroll_back":         { "type": "boolean", "default": true },
          "scroll_start_delay":  { "type": "integer", "minimum": 0, "maximum": 10000 },
          "scroll_back_duration":{ "type": "integer", "minimum": 200, "maximum": 5000, "default": 1200 },
          "scroll_by":           { "type": "integer", "minimum": 50, "maximum": 5000, "description": "Stepped mode only" },
          "scroll_delay":        { "type": "integer", "minimum": 0,  "maximum": 5000, "description": "Stepped mode only" },
          "scroll_duration":     { "type": "integer", "minimum": 100,"maximum": 5000, "description": "Stepped mode only" },
          "viewport_width":      { "type": "integer", "minimum": 320, "maximum": 1920, "default": 1280 },
          "viewport_height":     { "type": "integer", "minimum": 240, "maximum": 1080, "default": 720 },
          "block_ads":           { "type": "boolean", "default": true },
          "block_cookie_banners":{ "type": "boolean", "default": true },
          "gif_fps":             { "type": "integer", "minimum": 8,  "maximum": 24,   "default": 12 },
          "gif_width":           { "type": "integer", "minimum": 240,"maximum": 1280, "default": 720 },
          "webhook_url":         { "type": "string", "format": "uri" },
          "webhook_secret":      { "type": "string", "maxLength": 256 },
          "async":               { "type": "boolean", "default": true, "description": "Force sync with false (max wait 90s)" }
        }
      },
      "AnimateResponse": {
        "allOf": [
          { "$ref": "#/components/schemas/ScreenshotResponse" },
          {
            "type": "object",
            "properties": {
              "scenario":         { "type": "string" },
              "duration_seconds": { "type": "integer" },
              "auto_duration":    { "type": "boolean" },
              "max_duration":     { "type": "integer" }
            }
          }
        ]
      },
      "AsyncJobResponse": {
        "type": "object",
        "properties": {
          "job_id":             { "type": "string" },
          "db_id":              { "type": "integer" },
          "status":             { "type": "string", "enum": ["queued"] },
          "status_url":         { "type": "string", "format": "uri" },
          "credits_charged":    { "type": "integer" },
          "credits_remaining":  { "type": "integer" },
          "webhook_configured": { "type": "boolean" }
        }
      },
      "ProcessingResponse": {
        "type": "object",
        "properties": {
          "status":  { "type": "string", "enum": ["processing"] },
          "job_id":  { "type": "string" },
          "db_id":   { "type": "integer" },
          "message": { "type": "string" }
        }
      },
      "ScreenshotStatus": {
        "type": "object",
        "properties": {
          "db_id":           { "type": "integer" },
          "status":          { "type": "string", "enum": ["queued","processing","done","failed","expired"] },
          "input_url":       { "type": "string" },
          "url":             { "type": "string", "nullable": true },
          "expires_at":      { "type": "string", "format": "date-time", "nullable": true },
          "format":          { "type": "string" },
          "size_bytes":      { "type": "integer", "nullable": true },
          "width":           { "type": "integer", "nullable": true },
          "height":          { "type": "integer", "nullable": true },
          "duration_ms":     { "type": "integer", "nullable": true },
          "credits_charged": { "type": "integer" },
          "error_code":      { "type": "string", "nullable": true },
          "error_message":   { "type": "string", "nullable": true },
          "created_at":      { "type": "string", "format": "date-time" },
          "completed_at":    { "type": "string", "format": "date-time", "nullable": true }
        }
      },
      "BulkRequest": {
        "type": "object",
        "required": ["urls"],
        "properties": {
          "urls":           { "type": "array", "items": { "type": "string", "format": "uri" }, "maxItems": 500 },
          "format":         { "type": "string", "enum": ["png","jpg","jpeg","webp","pdf"], "default": "png" },
          "viewport_width": { "type": "integer" },
          "viewport_height":{ "type": "integer" },
          "full_page":      { "type": "boolean" },
          "block_ads":      { "type": "boolean" },
          "block_cookie_banners": { "type": "boolean" },
          "delay":          { "type": "integer" },
          "selector":       { "type": "string" },
          "device":         { "type": "string" },
          "webhook_url":    { "type": "string", "format": "uri" },
          "webhook_secret": { "type": "string", "maxLength": 256 }
        }
      },
      "BulkResponse": {
        "type": "object",
        "properties": {
          "batch_id":           { "type": "string", "pattern": "^bat_[a-f0-9]{32}$" },
          "status":             { "type": "string", "enum": ["queued"] },
          "total":              { "type": "integer" },
          "credits_charged":    { "type": "integer" },
          "credits_remaining":  { "type": "integer" },
          "webhook_configured": { "type": "boolean" },
          "status_url":         { "type": "string", "format": "uri" }
        }
      },
      "BatchStatus": {
        "type": "object",
        "properties": {
          "batch_id":     { "type": "string" },
          "status":       { "type": "string", "enum": ["queued","processing","completed","failed"] },
          "total":        { "type": "integer" },
          "processed":    { "type": "integer" },
          "failed":       { "type": "integer" },
          "progress":     { "type": "number", "format": "float", "minimum": 0, "maximum": 1 },
          "created_at":   { "type": "string", "format": "date-time" },
          "started_at":   { "type": "string", "format": "date-time", "nullable": true },
          "completed_at": { "type": "string", "format": "date-time", "nullable": true },
          "has_webhook":  { "type": "boolean" },
          "results":      { "type": "array", "nullable": true, "items": { "$ref": "#/components/schemas/ScreenshotStatus" } }
        }
      },
      "CreditsResponse": {
        "type": "object",
        "properties": {
          "plan_code":      { "type": "string", "enum": ["free","basic","growth","scale"] },
          "plan_label":     { "type": "string" },
          "plan_quota":     { "type": "integer" },
          "plan_used":      { "type": "integer" },
          "plan_remaining": { "type": "integer" },
          "resets_at":      { "type": "string", "format": "date-time" },
          "plan_status":    { "type": "string" },
          "total_consumed": { "type": "integer" },
          "upgrade_url":    { "type": "string", "format": "uri" },
          "billing_url":    { "type": "string", "format": "uri" }
        }
      },
      "DemoRequest": {
        "type": "object",
        "required": ["url"],
        "properties": {
          "url": { "type": "string", "format": "uri" }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error":   { "type": "string", "description": "Stable error code (parseable)" },
          "message": { "type": "string", "description": "Human-readable explanation" }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid API key",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "NotFound": {
        "description": "Resource not found",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "QuotaExceeded": {
        "description": "Monthly quota exhausted",
        "content": { "application/json": { "schema": {
          "allOf": [
            { "$ref": "#/components/schemas/Error" },
            {
              "type": "object",
              "properties": {
                "plan_code":      { "type": "string" },
                "plan_quota":     { "type": "integer" },
                "plan_used":      { "type": "integer" },
                "plan_remaining": { "type": "integer" },
                "credits_needed": { "type": "integer" },
                "resets_at":      { "type": "string", "format": "date-time" },
                "upgrade_url":    { "type": "string", "format": "uri" }
              }
            }
          ]
        } } }
      },
      "InvalidInput": {
        "description": "Validation error on input",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "RateLimited": {
        "description": "Rate limit hit (per-key RPM)",
        "headers": {
          "Retry-After":            { "schema": { "type": "integer" }, "description": "Seconds to wait before retry" },
          "X-RateLimit-Limit":      { "schema": { "type": "integer" } },
          "X-RateLimit-Remaining":  { "schema": { "type": "integer" } },
          "X-RateLimit-Reset":      { "schema": { "type": "integer" } }
        },
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "TargetError": {
        "description": "Target site returned an error (DNS, SSL, blocked, etc.)",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "Timeout": {
        "description": "Target site timed out",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    }
  }
}
