{
  "openapi": "3.1.0",
  "info": {
    "title": "urlshot.io Screenshot API",
    "version": "1.2.0",
    "summary": "Capture a screenshot of any public web page over HTTP.",
    "description": "Authenticate with a secret key using `Authorization: Bearer sk_...`.\n\nAlternatively, sign the query string and pass a publishable key (`pk_...`) as the\n`key` parameter, which needs\nno header and is therefore safe to embed in an `<img>` tag. See the `signature` security\nscheme for the payload construction. Signed URLs are `GET` only.\n\nOptions travel as query parameters on `GET`, or as a JSON object in the body of a `POST`.\nThe two are equivalent -- the same names, validation, price and cache -- and `POST` suits\n`custom_css` and `custom_js` better: source code is an ordinary JSON string rather than\npercent-encoded, it stays out of the URL that logs and proxies record, and text outside\nASCII cannot push the request past the 64 KB URL limit of the edge in front of the API.\n\nA successful request returns the image bytes directly. Any request that cannot return an\nimage returns the JSON error envelope described by the `Error` schema.\n\nCaching is opt in. `cache_ttl=0`, the default, always renders fresh. A cache hit consumes\nzero render credits; a render attempt that reaches the browser consumes exactly one,\neven if the target page then fails to load."
  },
  "servers": [
    {
      "url": "https://api.urlshot.io",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    },
    {
      "signedUrlKey": [],
      "signedUrlSignature": []
    }
  ],
  "tags": [
    {
      "name": "Screenshots",
      "description": "Synchronous screenshot capture."
    }
  ],
  "paths": {
    "/v1/screenshot": {
      "get": {
        "operationId": "getScreenshot",
        "tags": [
          "Screenshots"
        ],
        "summary": "Capture a screenshot",
        "description": "Renders the target URL and returns the encoded image. Options that affect the produced image also affect the cache key.\n\nThe same request can be sent as `POST` with a JSON body, which is the better choice when it carries `custom_css` or `custom_js`.\n\n### Rendering environment\n\nPages are rendered by headless Chromium on a machine with no GPU.\n\n- **WebGL and GPU-accelerated canvas are unavailable.** A page depending on them renders its fallback, or an empty area, and still returns `200`. Charting and mapping libraries that fall back to 2D canvas or SVG capture correctly; those requiring WebGL do not.\n- **Only these font families are installed:** Liberation (metric-compatible with Arial, Times New Roman and Courier New), Noto Sans, Noto CJK and Noto Color Emoji. Any other family is substituted, so text may wrap and lay out differently from your own browser. Web fonts the page loads over the network are used normally.\n\nNone of these produce an error, so a screenshot that looks wrong rather than missing is usually one of them.",
        "parameters": [
          {
            "name": "url",
            "in": "query",
            "required": true,
            "description": "Absolute HTTP or HTTPS URL of the page to capture.",
            "schema": {
              "format": "uri",
              "maxLength": 2048,
              "type": "string"
            }
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "description": "Output image format.",
            "schema": {
              "default": "png",
              "enum": [
                "png",
                "jpeg",
                "webp"
              ],
              "type": "string"
            }
          },
          {
            "name": "viewport_width",
            "in": "query",
            "required": false,
            "description": "Viewport width in CSS pixels.",
            "schema": {
              "default": 1280,
              "maximum": 3840,
              "minimum": 200,
              "type": "integer"
            }
          },
          {
            "name": "viewport_height",
            "in": "query",
            "required": false,
            "description": "Viewport height in CSS pixels. Ignored for the captured height when full_page is true.",
            "schema": {
              "default": 720,
              "maximum": 4320,
              "minimum": 200,
              "type": "integer"
            }
          },
          {
            "name": "device_scale_factor",
            "in": "query",
            "required": false,
            "description": "Device pixel ratio to emulate.",
            "schema": {
              "default": 1,
              "maximum": 3,
              "minimum": 0.5,
              "type": "number"
            }
          },
          {
            "name": "full_page",
            "in": "query",
            "required": false,
            "description": "Capture the full scrollable page instead of only the viewport.",
            "schema": {
              "default": false,
              "type": "boolean"
            }
          },
          {
            "name": "quality",
            "in": "query",
            "required": false,
            "description": "Encoder quality for jpeg and webp. Defaults to 80 for those formats and must be omitted for png.",
            "schema": {
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "name": "wait_until",
            "in": "query",
            "required": false,
            "description": "Navigation lifecycle event to wait for before capturing.",
            "schema": {
              "default": "load",
              "enum": [
                "load",
                "domcontentloaded",
                "networkidle0",
                "networkidle2"
              ],
              "type": "string"
            }
          },
          {
            "name": "delay_ms",
            "in": "query",
            "required": false,
            "description": "Additional wait after the navigation condition is met. Also bounded by the plan.",
            "schema": {
              "default": 0,
              "maximum": 10000,
              "minimum": 0,
              "type": "integer"
            }
          },
          {
            "name": "timeout_ms",
            "in": "query",
            "required": false,
            "description": "Maximum navigation time. Also bounded by the plan.",
            "schema": {
              "default": 15000,
              "maximum": 30000,
              "minimum": 1000,
              "type": "integer"
            }
          },
          {
            "name": "dark_mode",
            "in": "query",
            "required": false,
            "description": "Emulate prefers-color-scheme: dark before navigation.",
            "schema": {
              "default": false,
              "type": "boolean"
            }
          },
          {
            "name": "hide_selectors",
            "in": "query",
            "required": false,
            "description": "CSS selector whose matching elements are hidden after navigation. Repeatable.",
            "style": "form",
            "explode": true,
            "schema": {
              "items": {
                "maxLength": 200,
                "type": "string"
              },
              "maxItems": 20,
              "type": "array"
            }
          },
          {
            "name": "block_cookie_banners",
            "in": "query",
            "required": false,
            "description": "Hide known consent dialogs and restore page scrolling before capturing. Available on every plan.",
            "schema": {
              "default": false,
              "type": "boolean"
            }
          },
          {
            "name": "custom_css",
            "in": "query",
            "required": false,
            "description": "CSS injected into the page after it loads, as the last stylesheet.",
            "schema": {
              "maxLength": 4096,
              "type": "string"
            }
          },
          {
            "name": "custom_js",
            "in": "query",
            "required": false,
            "description": "JavaScript evaluated in the page before the capture. Paid plans only.",
            "schema": {
              "maxLength": 4096,
              "type": "string"
            }
          },
          {
            "name": "cache_ttl",
            "in": "query",
            "required": false,
            "description": "Seconds an identical request may be served from cache. 0 bypasses the cache.",
            "schema": {
              "default": 0,
              "maximum": 86400,
              "minimum": 0,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The captured screenshot.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              },
              "X-Urlshot-Cache": {
                "$ref": "#/components/headers/X-Urlshot-Cache"
              },
              "X-Urlshot-Credits-Used": {
                "$ref": "#/components/headers/X-Urlshot-Credits-Used"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-Urlshot-Renderer": {
                "$ref": "#/components/headers/X-Urlshot-Renderer"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/Cache-Control"
              }
            },
            "content": {
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "image/jpeg": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "image/webp": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "The request could not be validated, or the target address is not permitted. Codes: `invalid_request`, `target_not_allowed`, `script_failed`.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              },
              "X-Urlshot-Cache": {
                "$ref": "#/components/headers/X-Urlshot-Cache"
              },
              "X-Urlshot-Credits-Used": {
                "$ref": "#/components/headers/X-Urlshot-Credits-Used"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, invalid, or revoked. Codes: `invalid_api_key`, `revoked_api_key`, `invalid_signature`.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              },
              "X-Urlshot-Cache": {
                "$ref": "#/components/headers/X-Urlshot-Cache"
              },
              "X-Urlshot-Credits-Used": {
                "$ref": "#/components/headers/X-Urlshot-Credits-Used"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "A rate, concurrency, or monthly credit limit was reached. Codes: `rate_limit_exceeded`, `concurrency_limit_exceeded`, `monthly_limit_exceeded`.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              },
              "X-Urlshot-Cache": {
                "$ref": "#/components/headers/X-Urlshot-Cache"
              },
              "X-Urlshot-Credits-Used": {
                "$ref": "#/components/headers/X-Urlshot-Credits-Used"
              },
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The target page could not be loaded or captured. Codes: `navigation_failed`, `render_failed`.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              },
              "X-Urlshot-Cache": {
                "$ref": "#/components/headers/X-Urlshot-Cache"
              },
              "X-Urlshot-Credits-Used": {
                "$ref": "#/components/headers/X-Urlshot-Credits-Used"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The screenshot service is temporarily unavailable. Codes: `service_unavailable`.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              },
              "X-Urlshot-Cache": {
                "$ref": "#/components/headers/X-Urlshot-Cache"
              },
              "X-Urlshot-Credits-Used": {
                "$ref": "#/components/headers/X-Urlshot-Credits-Used"
              },
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "The render exceeded the effective timeout. Codes: `render_timeout`.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              },
              "X-Urlshot-Cache": {
                "$ref": "#/components/headers/X-Urlshot-Cache"
              },
              "X-Urlshot-Credits-Used": {
                "$ref": "#/components/headers/X-Urlshot-Credits-Used"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "postScreenshot",
        "tags": [
          "Screenshots"
        ],
        "summary": "Capture a screenshot, with options in a JSON body",
        "description": "The `GET` operation with its options in a JSON body instead of the query string. The response, the price and the cache are the same: a `GET` and a `POST` with the same options share one cache entry.\n\nSend `Content-Type: application/json` and no query parameters. The body may be at most 71536 bytes, which admits every option at its limit however the JSON is escaped.\n\nBearer keys only: a signed URL signs its query string, and a body is not part of it.\n\nThe rendering environment is described under the `GET` operation.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ScreenshotRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The captured screenshot.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              },
              "X-Urlshot-Cache": {
                "$ref": "#/components/headers/X-Urlshot-Cache"
              },
              "X-Urlshot-Credits-Used": {
                "$ref": "#/components/headers/X-Urlshot-Credits-Used"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-Urlshot-Renderer": {
                "$ref": "#/components/headers/X-Urlshot-Renderer"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/Cache-Control"
              }
            },
            "content": {
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "image/jpeg": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "image/webp": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "The request could not be validated, or the target address is not permitted. Codes: `invalid_request`, `target_not_allowed`, `script_failed`.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              },
              "X-Urlshot-Cache": {
                "$ref": "#/components/headers/X-Urlshot-Cache"
              },
              "X-Urlshot-Credits-Used": {
                "$ref": "#/components/headers/X-Urlshot-Credits-Used"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, invalid, or revoked. Codes: `invalid_api_key`, `revoked_api_key`, `invalid_signature`.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              },
              "X-Urlshot-Cache": {
                "$ref": "#/components/headers/X-Urlshot-Cache"
              },
              "X-Urlshot-Credits-Used": {
                "$ref": "#/components/headers/X-Urlshot-Credits-Used"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "The request body is larger than 71536 bytes. Code: `invalid_request`.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              },
              "X-Urlshot-Cache": {
                "$ref": "#/components/headers/X-Urlshot-Cache"
              },
              "X-Urlshot-Credits-Used": {
                "$ref": "#/components/headers/X-Urlshot-Credits-Used"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "The request body is not sent as `Content-Type: application/json`. Code: `invalid_request`.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              },
              "X-Urlshot-Cache": {
                "$ref": "#/components/headers/X-Urlshot-Cache"
              },
              "X-Urlshot-Credits-Used": {
                "$ref": "#/components/headers/X-Urlshot-Credits-Used"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "A rate, concurrency, or monthly credit limit was reached. Codes: `rate_limit_exceeded`, `concurrency_limit_exceeded`, `monthly_limit_exceeded`.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              },
              "X-Urlshot-Cache": {
                "$ref": "#/components/headers/X-Urlshot-Cache"
              },
              "X-Urlshot-Credits-Used": {
                "$ref": "#/components/headers/X-Urlshot-Credits-Used"
              },
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The target page could not be loaded or captured. Codes: `navigation_failed`, `render_failed`.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              },
              "X-Urlshot-Cache": {
                "$ref": "#/components/headers/X-Urlshot-Cache"
              },
              "X-Urlshot-Credits-Used": {
                "$ref": "#/components/headers/X-Urlshot-Credits-Used"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The screenshot service is temporarily unavailable. Codes: `service_unavailable`.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              },
              "X-Urlshot-Cache": {
                "$ref": "#/components/headers/X-Urlshot-Cache"
              },
              "X-Urlshot-Credits-Used": {
                "$ref": "#/components/headers/X-Urlshot-Credits-Used"
              },
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "The render exceeded the effective timeout. Codes: `render_timeout`.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/X-Request-ID"
              },
              "X-Urlshot-Cache": {
                "$ref": "#/components/headers/X-Urlshot-Cache"
              },
              "X-Urlshot-Credits-Used": {
                "$ref": "#/components/headers/X-Urlshot-Credits-Used"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "A secret key (`sk_...`), shown once at creation and never retrievable again. A publishable key is refused here: it belongs in a signed URL, not in a header."
      },
      "signedUrlKey": {
        "type": "apiKey",
        "in": "query",
        "name": "key",
        "description": "A publishable key (`pk_...`), carried in the query so the URL needs no header. Included in the signed payload, so a signature cannot be replayed against another key. A secret key is refused here."
      },
      "signedUrlSignature": {
        "type": "apiKey",
        "in": "query",
        "name": "signature",
        "description": "Hex HMAC-SHA-256 over the request's own query string, keyed by the signing secret\nissued with the API key.\n\nThe signed payload is the query string exactly as it will be sent, with the\n`signature` pair removed. Nothing is prepended, and parameters are **not** sorted or\nnormalised: sign the exact bytes you are about to send."
      }
    },
    "headers": {
      "X-Request-ID": {
        "description": "Opaque identifier for this request. Quote it in any support conversation.",
        "schema": {
          "type": "string"
        }
      },
      "X-Urlshot-Cache": {
        "description": "HIT when the image was served from cache, MISS when it was rendered, BYPASS when cache_ttl was 0.",
        "schema": {
          "type": "string",
          "enum": [
            "HIT",
            "MISS",
            "BYPASS"
          ]
        }
      },
      "X-Urlshot-Credits-Used": {
        "description": "Render credits consumed by this request. A cache hit consumes zero.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "X-RateLimit-Limit": {
        "description": "Renders your plan may run at once.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "X-Urlshot-Renderer": {
        "description": "The renderer build that produced the image, such as v4:4572cafff9ea; on a cache hit, the build that made the cached image. For support conversations, not for branching on.",
        "schema": {
          "type": "string"
        }
      },
      "X-RateLimit-Remaining": {
        "description": "Render slots still free after this request. Absent on a cache hit, where it is unknown rather than zero.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "Cache-Control": {
        "description": "How long a browser may reuse the image: private, max-age=<seconds> when cache_ttl is set, and on a cache hit no longer than the cached copy has left; no-store when cache_ttl is 0.",
        "schema": {
          "type": "string"
        }
      }
    },
    "schemas": {
      "ScreenshotRequest": {
        "type": "object",
        "description": "The options of `GET /v1/screenshot`, as a JSON object. Every property has the name, bounds\nand default of the query parameter it replaces, and values take their JSON type: `true`,\nnot `\"true\"`. A property set to `null` is treated as absent, and an unknown property is an\nerror, as an unknown query parameter is.",
        "required": [
          "url"
        ],
        "additionalProperties": false,
        "properties": {
          "url": {
            "description": "Absolute HTTP or HTTPS URL of the page to capture.",
            "format": "uri",
            "maxLength": 2048,
            "type": "string"
          },
          "format": {
            "default": "png",
            "description": "Output image format.",
            "enum": [
              "png",
              "jpeg",
              "webp"
            ],
            "type": "string"
          },
          "viewport_width": {
            "default": 1280,
            "description": "Viewport width in CSS pixels.",
            "maximum": 3840,
            "minimum": 200,
            "type": "integer"
          },
          "viewport_height": {
            "default": 720,
            "description": "Viewport height in CSS pixels. Ignored for the captured height when full_page is true.",
            "maximum": 4320,
            "minimum": 200,
            "type": "integer"
          },
          "device_scale_factor": {
            "default": 1,
            "description": "Device pixel ratio to emulate.",
            "maximum": 3,
            "minimum": 0.5,
            "type": "number"
          },
          "full_page": {
            "default": false,
            "description": "Capture the full scrollable page instead of only the viewport.",
            "type": "boolean"
          },
          "quality": {
            "description": "Encoder quality for jpeg and webp. Defaults to 80 for those formats and must be omitted for png.",
            "maximum": 100,
            "minimum": 1,
            "type": "integer"
          },
          "wait_until": {
            "default": "load",
            "description": "Navigation lifecycle event to wait for before capturing.",
            "enum": [
              "load",
              "domcontentloaded",
              "networkidle0",
              "networkidle2"
            ],
            "type": "string"
          },
          "delay_ms": {
            "default": 0,
            "description": "Additional wait after the navigation condition is met. Also bounded by the plan.",
            "maximum": 10000,
            "minimum": 0,
            "type": "integer"
          },
          "timeout_ms": {
            "default": 15000,
            "description": "Maximum navigation time. Also bounded by the plan.",
            "maximum": 30000,
            "minimum": 1000,
            "type": "integer"
          },
          "dark_mode": {
            "default": false,
            "description": "Emulate prefers-color-scheme: dark before navigation.",
            "type": "boolean"
          },
          "hide_selectors": {
            "description": "CSS selectors whose matching elements are hidden after navigation.",
            "items": {
              "maxLength": 200,
              "type": "string"
            },
            "maxItems": 20,
            "type": "array"
          },
          "block_cookie_banners": {
            "default": false,
            "description": "Hide known consent dialogs and restore page scrolling before capturing. Available on every plan.",
            "type": "boolean"
          },
          "custom_css": {
            "description": "CSS injected into the page after it loads, as the last stylesheet.",
            "maxLength": 4096,
            "type": "string"
          },
          "custom_js": {
            "description": "JavaScript evaluated in the page before the capture. Paid plans only.",
            "maxLength": 4096,
            "type": "string"
          },
          "cache_ttl": {
            "default": 0,
            "description": "Seconds an identical request may be served from cache. 0 bypasses the cache.",
            "maximum": 86400,
            "minimum": 0,
            "type": "integer"
          }
        }
      },
      "ErrorCode": {
        "type": "string",
        "description": "Stable machine-readable error identifier.",
        "enum": [
          "invalid_request",
          "invalid_api_key",
          "revoked_api_key",
          "invalid_signature",
          "rate_limit_exceeded",
          "concurrency_limit_exceeded",
          "monthly_limit_exceeded",
          "target_not_allowed",
          "navigation_failed",
          "render_timeout",
          "render_failed",
          "script_failed",
          "service_unavailable"
        ]
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "requestId"
            ],
            "properties": {
              "code": {
                "$ref": "#/components/schemas/ErrorCode"
              },
              "message": {
                "type": "string",
                "description": "Human-readable explanation. Never contains a value you supplied. A rejected parameter is named, and an unrecognised name is repeated back only when it is a short identifier, so the message is always safe to log and to display."
              },
              "requestId": {
                "type": "string",
                "description": "Matches the X-Request-ID response header."
              }
            }
          }
        },
        "examples": [
          {
            "error": {
              "code": "monthly_limit_exceeded",
              "message": "The workspace has used all credits for the current period.",
              "requestId": "req_0123456789abcdef0123456789abcdef"
            }
          }
        ]
      }
    }
  }
}
