{
  "openapi": "3.1.0",
  "info": {
    "title": "zpe-scraper",
    "version": "5.0.0",
    "description": "A scraping API with an SSRF guard in front of it: one URL in, clean markdown out, metered by browser time. Every operation lives under the versioned base /_/api/v1 and the document below is served at /_/api/v1/openapi.json. The unversioned machine paths this app answered before the move (/v1/scrape and its siblings) are 308 signposts at their versioned address."
  },
  "servers": [
    {
      "url": "",
      "description": "zpe-scraper on zpe.vc"
    }
  ],
  "paths": {
    "/_/api/v1/scrape": {
      "post": {
        "operationId": "postScrape",
        "summary": "POST /_/api/v1/scrape",
        "tags": [
          "scrape"
        ],
        "requestBody": {
          "required": true,
          "description": "The page to read, and which readings of it to return.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "The absolute address to scrape. The SSRF guard reads it before any browser opens, so a refused address costs nothing."
                  },
                  "formats": {
                    "type": "array",
                    "minItems": 1,
                    "items": {
                      "type": "string",
                      "enum": [
                        "markdown",
                        "html",
                        "rawHtml",
                        "links",
                        "screenshot",
                        "screenshot@fullPage"
                      ]
                    },
                    "description": "Which readings to return. Defaults to markdown, html, rawHtml and links; duplicates are dropped."
                  },
                  "onlyMainContent": {
                    "type": "boolean",
                    "description": "Drop navigation, footers and other page chrome from the text. Defaults to true."
                  },
                  "waitFor": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 30000,
                    "description": "Milliseconds to wait after the page loads before reading it. Defaults to 0."
                  },
                  "timeout": {
                    "type": "integer",
                    "minimum": 1000,
                    "description": "The job's ceiling in milliseconds. Defaults to 60000 or the deployment's MAX_JOB_MS, whichever is lower."
                  },
                  "headers": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    },
                    "description": "Extra request headers for the target, at most 20. Host and hop-by-hop headers are refused."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The page, read into the formats asked for, with the browser time it cost and how that time was measured.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "The request could not be read, or the address was refused before any fetch: a blocked host, a blocked address, a scheme other than http or https, or credentials in the URL.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "The credential is missing, or the door did not recognise it.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "402": {
            "description": "Payment required: this account holds no credit, or its balance cannot cover the browser time a scrape reserves before it starts.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "This key may not do that: it is granted a different app, its scope does not cover this verb, or the request was refused by the site's own robots.txt.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "The page could not be read: the address did not resolve, the redirect chain was too long, the body exceeded the size cap, or the driver reported an error.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: this key has spent its request ceiling, or this account already has its browser slots open. Retry after the period the answer names.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "This app failed while answering",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "502": {
            "description": "The browser the scrape opened did not answer, so nothing could be read.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "A service this app needs did not answer: the door could not check the credential, the ledger binding a metered call needs is absent, or the browser the scrape needs is not bound here.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "504": {
            "description": "The address did not answer in the time the request allowed.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/_/api/v1/map": {
      "post": {
        "operationId": "postMap",
        "summary": "POST /_/api/v1/map",
        "tags": [
          "map"
        ],
        "requestBody": {
          "required": true,
          "description": "The address to read links from.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "The absolute address whose links are listed. Answered from one guarded fetch, with no browser."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The links found at that address, filtered by the request, with the metered time the listing cost.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`url` is missing or is not a string, or the address was refused before any fetch: a blocked host, a blocked address, a scheme other than http or https, or credentials in the URL.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "The credential is missing, or the door did not recognise it.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "402": {
            "description": "Payment required: this account holds no credit, or its balance cannot cover the time a listing reserves before it starts.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "This key may not do that: it is granted a different app, its scope does not cover this verb, or the request was refused by the site's own robots.txt.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "The page could not be read: the address did not resolve, the redirect chain was too long, or the body exceeded the size cap.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: this key has spent its request ceiling. Retry after the period the answer names.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "This app failed while answering",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "A service this app needs did not answer: the door could not check the credential, the ledger binding a metered call needs is absent, or the browser the scrape needs is not bound here.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/_/api/v1/balance": {
      "get": {
        "operationId": "getBalance",
        "summary": "GET /_/api/v1/balance",
        "tags": [
          "balance"
        ],
        "responses": {
          "200": {
            "description": "The credit this account holds, in milliseconds of browser time, and whether it is live or only observed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "The credential is missing, or the door did not recognise it.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "This key may not do that: it is granted a different app, its scope does not cover this verb, or the request was refused by the site's own robots.txt.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: this key has spent its request ceiling. Retry after the period the answer names.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "This app failed while answering",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "A service this app needs did not answer: the door could not check the credential, the ledger binding a metered call needs is absent, or the browser the scrape needs is not bound here.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/_/api/v1/usage": {
      "get": {
        "operationId": "getUsage",
        "summary": "GET /_/api/v1/usage",
        "tags": [
          "usage"
        ],
        "responses": {
          "200": {
            "description": "This account's twenty most recent scrapes, newest first, with the address, time and charge of each.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "The credential is missing, or the door did not recognise it.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "This key may not do that: it is granted a different app, its scope does not cover this verb, or the request was refused by the site's own robots.txt.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: this key has spent its request ceiling. Retry after the period the answer names.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "This app failed while answering",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "A service this app needs did not answer: the door could not check the credential, the ledger binding a metered call needs is absent, or the browser the scrape needs is not bound here.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/_/api/v1/keys": {
      "get": {
        "operationId": "getKeys",
        "summary": "GET /_/api/v1/keys",
        "tags": [
          "keys"
        ],
        "responses": {
          "200": {
            "description": "Every key this account has minted, with its label, when it was created and last used, and whether it is revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "The credential is missing, or the door did not recognise it.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "This key may not do that: it is granted a different app, its scope does not cover this verb, or the request was refused by the site's own robots.txt.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: this key has spent its request ceiling. Retry after the period the answer names.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "This app failed while answering",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "A service this app needs did not answer: the door could not check the credential, the ledger binding a metered call needs is absent, or the browser the scrape needs is not bound here.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "postKeys",
        "summary": "POST /_/api/v1/keys",
        "tags": [
          "keys"
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "A retry handle: a second POST carrying the same value answers `200` with the first call's key and label, `secret: null`, and mints nothing.",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          }
        ],
        "requestBody": {
          "required": false,
          "description": "An optional label for the key.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "label": {
                    "type": "string",
                    "description": "A name for the key, shown in `GET /_/api/v1/keys`. A live key with the same label is refused rather than duplicated."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A replay: this `Idempotency-Key` was already answered. The same key id and label the first call returned, `secret: null`, and nothing minted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "201": {
            "description": "The key was minted. The secret is in this answer and nowhere else: it is stored as a keyed digest.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "A live key with that label already exists, the `Idempotency-Key` was empty or longer than 200 characters, or the key could not be minted for a reason the answer names.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "The credential is missing, or the door did not recognise it.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "This key may not do that: it is granted a different app, its scope does not cover this verb, or the request was refused by the site's own robots.txt.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: this key has spent its request ceiling. Retry after the period the answer names.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "This app failed while answering",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "Keys cannot be minted here: this deployment has no token pepper or no store, or the door could not check the credential.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/_/api/v1/keys/{id}": {
      "delete": {
        "operationId": "deleteKeysById",
        "summary": "DELETE /_/api/v1/keys/:id",
        "tags": [
          "keys"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The key's id, as `GET /v1/keys` answered it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The key is revoked. A second call for the same id answers `404`: there is no live key left to revoke.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "The credential is missing, or the door did not recognise it.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "This key may not do that: it is granted a different app, its scope does not cover this verb, or the request was refused by the site's own robots.txt.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No live key with that id belongs to this account.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: this key has spent its request ceiling. Retry after the period the answer names.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "This app failed while answering",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "A service this app needs did not answer: the door could not check the credential, the ledger binding a metered call needs is absent, or the browser the scrape needs is not bound here.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/_/api/v1/domains": {
      "get": {
        "operationId": "getDomains",
        "summary": "GET /_/api/v1/domains",
        "tags": [
          "domains"
        ],
        "responses": {
          "200": {
            "description": "Every domain this account has claimed, with the state of each claim and the TXT record it was proved with.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "The credential is missing, or the door did not recognise it.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "This key may not do that: it is granted a different app, its scope does not cover this verb, or the request was refused by the site's own robots.txt.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: this key has spent its request ceiling. Retry after the period the answer names.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "This app failed while answering",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "A service this app needs did not answer: the door could not check the credential, the ledger binding a metered call needs is absent, or the browser the scrape needs is not bound here.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      },
      "post": {
        "operationId": "postDomains",
        "summary": "POST /_/api/v1/domains",
        "tags": [
          "domains"
        ],
        "requestBody": {
          "required": true,
          "description": "The hostname to claim, so its robots.txt rules can be overridden for this account.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "domain"
                ],
                "properties": {
                  "domain": {
                    "type": "string",
                    "description": "A bare hostname like `example.com`. It is normalized to lower case, and a scheme or a path is refused."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The claim exists: proved, already proved, or waiting for the TXT record the answer names.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "The body could not be read, or `domain` is not a bare hostname like example.com.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "The credential is missing, or the door did not recognise it.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "This key may not do that: it is granted a different app, its scope does not cover this verb, or the request was refused by the site's own robots.txt.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: this key has spent its request ceiling. Retry after the period the answer names.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "This app failed while answering",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "A service this app needs did not answer: the door could not check the credential, the ledger binding a metered call needs is absent, or the browser the scrape needs is not bound here.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    },
    "/_/api/v1/domains/{domain}": {
      "delete": {
        "operationId": "deleteDomainsById",
        "summary": "DELETE /_/api/v1/domains/:domain",
        "tags": [
          "domains"
        ],
        "parameters": [
          {
            "name": "domain",
            "in": "path",
            "description": "The bare hostname whose claim is dropped, as `GET /v1/domains` answered it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The claim is dropped, so the site's robots.txt rules apply to this account again.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "That is not a domain this route can read.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "The credential is missing, or the door did not recognise it.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "This key may not do that: it is granted a different app, its scope does not cover this verb, or the request was refused by the site's own robots.txt.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No claim for that domain belongs to this account.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests: this key has spent its request ceiling. Retry after the period the answer names.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "This app failed while answering",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "A service this app needs did not answer: the door could not check the credential, the ledger binding a metered call needs is absent, or the browser the scrape needs is not bound here.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "An API key minted on the account's settings page at zpe.vc/home/settings. Send it as `Authorization: Bearer <key>`."
      }
    },
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem+json: `type`, `title`, `status`, `code` and `instance`, plus `detail` when a person should read a sentence.",
        "required": [
          "type",
          "title",
          "status",
          "code",
          "instance"
        ],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "code": {
            "type": "string",
            "enum": [
              "bad-request",
              "unauthenticated",
              "malformed",
              "unknown",
              "mismatch",
              "revoked",
              "expired",
              "another-account",
              "scope-refused",
              "grant-refused",
              "forbidden",
              "not-found",
              "method-not-allowed",
              "conflict",
              "gone",
              "payload-too-large",
              "unsupported-media-type",
              "unprocessable",
              "rate-limited",
              "payment-required",
              "not-open",
              "internal",
              "unavailable"
            ]
          },
          "instance": {
            "type": "string"
          },
          "detail": {
            "type": "string"
          }
        }
      }
    }
  },
  "x-surface": {
    "app": "zpe-scraper",
    "base": "/_/api/v1",
    "document": "/_/api/v1/openapi.json",
    "generated_by": "zpe-kit machineDocument"
  },
  "x-zpe-routes": 12,
  "x-zpe-documented": 10,
  "x-zpe-internal": [
    "POST /v1/_probe/grant",
    "GET /v1/_probe/balance"
  ]
}
