{
  "openapi": "3.1.0",
  "info": {
    "title": "Forage API",
    "version": "1.0.0",
    "description": "Turn any web page or audio into AI-ready data.",
    "termsOfService": "https://forageapi.com/terms.html",
    "contact": {
      "email": "willchilcutt@gmail.com"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://forageapi.com",
      "description": "Production (coming soon)"
    },
    {
      "url": "https://forageapi.com",
      "description": "Current"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Keys",
      "description": "Create and manage API keys."
    },
    {
      "name": "Convert",
      "description": "Convert web pages into LLM-ready formats."
    },
    {
      "name": "Capture",
      "description": "Capture visual renders of web pages."
    },
    {
      "name": "Extract",
      "description": "Extract structured data from web pages."
    },
    {
      "name": "Audio",
      "description": "Transcribe audio into text."
    },
    {
      "name": "Reviews",
      "description": "Extract schema.org review data from web pages."
    },
    {
      "name": "Account",
      "description": "Account balance and usage."
    },
    {
      "name": "System",
      "description": "Service health."
    }
  ],
  "paths": {
    "/v1/keys": {
      "post": {
        "tags": [
          "Keys"
        ],
        "summary": "Create a free API key",
        "description": "Create a free API key. One key is allowed per email address, and each new key is seeded with 300 free credits. The full key is returned exactly once in this response and cannot be retrieved again. This endpoint is public and costs 0 credits.",
        "operationId": "createKey",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateKeyRequest"
              },
              "example": {
                "email": "developer@example.com"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "API key created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateKeyResponse"
                },
                "example": {
                  "key": "fk_live_9f8a7b6c5d4e3f2a1b0c9d8e7f6a5b4c",
                  "key_id": "key_ab12cd34ef56",
                  "credits": 300,
                  "message": "Store this key now — it is shown exactly once.",
                  "docs": "https://forageapi.com/docs"
                }
              }
            }
          },
          "400": {
            "description": "The email address is invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "invalid_email",
                  "message": "A valid email address is required."
                }
              }
            }
          },
          "409": {
            "description": "A key already exists for this email address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "email_exists",
                  "message": "An API key has already been issued for this email address."
                }
              }
            }
          },
          "429": {
            "description": "Too many key creation attempts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "rate_limited",
                  "message": "Too many requests. Please try again later."
                }
              }
            }
          }
        }
      }
    },
    "/v1/markdown": {
      "post": {
        "tags": [
          "Convert"
        ],
        "summary": "Clean LLM-ready markdown from a URL",
        "description": "Fetch a web page and return clean, LLM-ready markdown. Set `render` to control headless browser rendering: `auto` (default) renders only when needed, `never` fetches raw HTML, `always` forces a full browser render. Costs 1 credit. Failed operations are automatically refunded.",
        "operationId": "getMarkdown",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MarkdownRequest"
              },
              "example": {
                "url": "https://example.com/article",
                "render": "auto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Markdown extracted successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarkdownResponse"
                },
                "example": {
                  "url": "https://example.com/article",
                  "rendered": false,
                  "markdown": "# Example Article\n\nThis is the clean, LLM-ready markdown extracted from the page.\n\n- Point one\n- Point two",
                  "tokens_estimate": 512
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          }
        }
      }
    },
    "/v1/screenshot": {
      "post": {
        "tags": [
          "Capture"
        ],
        "summary": "Full PNG screenshot of a page",
        "description": "Render a web page in a headless browser and return a PNG screenshot. Use `full_page` to capture the entire scrollable page, and `wait_ms` (max 5000) to wait for late-loading content. Costs 1 credit. Failed operations are automatically refunded.",
        "operationId": "getScreenshot",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ScreenshotRequest"
              },
              "example": {
                "url": "https://example.com",
                "full_page": true,
                "width": 1280,
                "height": 800,
                "wait_ms": 500
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "PNG screenshot of the page.",
            "content": {
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          }
        }
      }
    },
    "/v1/pdf": {
      "post": {
        "tags": [
          "Capture"
        ],
        "summary": "Render a page to PDF (A4)",
        "description": "Render a web page in a headless browser and return an A4 PDF document. Costs 2 credits. Failed operations are automatically refunded.",
        "operationId": "getPdf",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PdfRequest"
              },
              "example": {
                "url": "https://example.com/invoice",
                "width": 1280,
                "height": 800,
                "wait_ms": 500
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "PDF render of the page.",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          }
        }
      }
    },
    "/v1/extract": {
      "post": {
        "tags": [
          "Extract"
        ],
        "summary": "Structured JSON from a page matching your JSON Schema",
        "description": "Extract structured JSON from a page. Provide either a `url` to fetch or raw `html`, plus a JSON Schema describing the data you want. The response `data` conforms to your supplied schema. Costs 2 credits. Failed operations are automatically refunded.",
        "operationId": "extract",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ExtractRequest"
              },
              "example": {
                "url": "https://shop.example.com/product/42",
                "schema": {
                  "type": "object",
                  "properties": {
                    "title": {
                      "type": "string"
                    },
                    "price": {
                      "type": "number"
                    }
                  },
                  "required": [
                    "title",
                    "price"
                  ]
                },
                "instructions": "Extract the product title and its current price.",
                "render": "auto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Structured data extracted successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExtractResponse"
                },
                "example": {
                  "url": "https://shop.example.com/product/42",
                  "rendered": false,
                  "data": {
                    "title": "Wireless Noise-Cancelling Headphones",
                    "price": 199.99
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request is missing required input.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "missing_input": {
                    "summary": "Neither url nor html was provided",
                    "value": {
                      "error": "missing_input",
                      "message": "Provide either a 'url' or 'html' to extract from."
                    }
                  },
                  "missing_schema": {
                    "summary": "No JSON Schema was provided",
                    "value": {
                      "error": "missing_schema",
                      "message": "A JSON Schema is required in the 'schema' field."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          }
        }
      }
    },
    "/v1/transcribe": {
      "post": {
        "tags": [
          "Audio"
        ],
        "summary": "Whisper transcript with word timestamps + segments",
        "description": "Transcribe an audio file with Whisper, returning full text, timed segments, and a WebVTT track. Provide a `url` pointing to an audio file (max 24MB). Costs a minimum of 2 credits, then 1 additional credit per started minute of audio. Failed operations are automatically refunded.",
        "operationId": "transcribe",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TranscribeRequest"
              },
              "example": {
                "url": "https://example.com/audio/episode-1.mp3"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Audio transcribed successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TranscribeResponse"
                },
                "example": {
                  "url": "https://example.com/audio/episode-1.mp3",
                  "duration_seconds": 92.5,
                  "credits_charged": 2,
                  "text": "Welcome to the show. Today we are talking about edge computing.",
                  "segments": [
                    {
                      "start": 0.0,
                      "end": 2.4,
                      "text": "Welcome to the show."
                    },
                    {
                      "start": 2.4,
                      "end": 6.1,
                      "text": "Today we are talking about edge computing."
                    }
                  ],
                  "vtt": "WEBVTT\n\n00:00:00.000 --> 00:00:02.400\nWelcome to the show.\n\n00:00:02.400 --> 00:00:06.100\nToday we are talking about edge computing."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "422": {
            "description": "The audio file is too large.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "audio_too_large",
                  "message": "Audio files must be 24MB or smaller."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          }
        }
      }
    },
    "/v1/reviews": {
      "post": {
        "tags": [
          "Reviews"
        ],
        "summary": "schema.org review + aggregateRating data from any page with JSON-LD",
        "description": "Extract schema.org Review and AggregateRating data from a page's JSON-LD markup. Costs 2 credits, but you are NOT charged if no review markup is found. Failed operations are automatically refunded.",
        "operationId": "getReviews",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReviewsRequest"
              },
              "example": {
                "url": "https://shop.example.com/product/42",
                "render": "auto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Review lookup completed. When no markup is found you are not charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReviewsResponse"
                },
                "examples": {
                  "found": {
                    "summary": "Review markup found",
                    "value": {
                      "url": "https://shop.example.com/product/42",
                      "rendered": false,
                      "subject": "Wireless Noise-Cancelling Headphones",
                      "aggregate": {
                        "rating": 4.6,
                        "count": 128
                      },
                      "count": 2,
                      "reviews": [
                        {
                          "author": "Jane D.",
                          "rating": 5,
                          "date": "2026-05-01",
                          "title": "Fantastic sound",
                          "body": "Best headphones I have owned."
                        },
                        {
                          "author": "Sam P.",
                          "rating": 4,
                          "date": "2026-04-22",
                          "title": "Great, minor gripes",
                          "body": "Comfortable but the app is clunky."
                        }
                      ]
                    }
                  },
                  "none_found": {
                    "summary": "No review markup found (not charged)",
                    "value": {
                      "url": "https://example.com/blog/post",
                      "rendered": false,
                      "reviews": [],
                      "aggregate": null,
                      "message": "No schema.org review markup found on this page. You were not charged."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          }
        }
      }
    },
    "/v1/usage": {
      "get": {
        "tags": [
          "Account"
        ],
        "summary": "Balance + recent usage",
        "description": "Return the current credit balance for your API key and a list of recent API calls. Costs 0 credits.",
        "operationId": "getUsage",
        "responses": {
          "200": {
            "description": "Current balance and recent usage.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UsageResponse"
                },
                "example": {
                  "key_id": "key_ab12cd34ef56",
                  "balance": 287,
                  "recent": [
                    {
                      "endpoint": "/v1/extract",
                      "credits": 2,
                      "created_at": "2026-07-02T14:31:07Z"
                    },
                    {
                      "endpoint": "/v1/markdown",
                      "credits": 1,
                      "created_at": "2026-07-02T14:28:55Z"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/health": {
      "get": {
        "tags": [
          "System"
        ],
        "summary": "Health check",
        "description": "Public liveness probe. Returns `{ \"ok\": true }` when the service is healthy. No authentication required and costs 0 credits.",
        "operationId": "getHealth",
        "security": [],
        "responses": {
          "200": {
            "description": "Service is healthy.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                },
                "example": {
                  "ok": true
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "fk_live_...",
        "description": "API key as Authorization: Bearer fk_live_..."
      }
    },
    "schemas": {
      "RenderMode": {
        "type": "string",
        "enum": [
          "auto",
          "never",
          "always"
        ],
        "default": "auto",
        "description": "Headless browser render mode. `auto` renders only when needed, `never` uses raw HTML, `always` forces a full render."
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "description": "Machine-readable error code."
          },
          "message": {
            "type": "string",
            "description": "Human-readable error message."
          }
        },
        "required": [
          "error"
        ]
      },
      "CreateKeyRequest": {
        "type": "object",
        "required": [
          "email"
        ],
        "properties": {
          "email": {
            "type": "string",
            "format": "email",
            "description": "The email address to associate with the new API key. One key is allowed per email."
          }
        }
      },
      "CreateKeyResponse": {
        "type": "object",
        "required": [
          "key",
          "key_id",
          "credits",
          "message",
          "docs"
        ],
        "properties": {
          "key": {
            "type": "string",
            "description": "The full API key. Shown exactly once — store it now.",
            "examples": [
              "fk_live_9f8a7b6c5d4e3f2a1b0c9d8e7f6a5b4c"
            ]
          },
          "key_id": {
            "type": "string",
            "description": "The public identifier for this key.",
            "examples": [
              "key_ab12cd34ef56"
            ]
          },
          "credits": {
            "type": "integer",
            "description": "Free credits seeded on the new key.",
            "examples": [
              300
            ]
          },
          "message": {
            "type": "string"
          },
          "docs": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "MarkdownRequest": {
        "type": "object",
        "required": [
          "url"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "The URL of the web page to convert."
          },
          "render": {
            "$ref": "#/components/schemas/RenderMode"
          }
        }
      },
      "MarkdownResponse": {
        "type": "object",
        "required": [
          "url",
          "rendered",
          "markdown",
          "tokens_estimate"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          },
          "rendered": {
            "type": "boolean",
            "description": "Whether the page was rendered in a headless browser."
          },
          "markdown": {
            "type": "string",
            "description": "Clean, LLM-ready markdown."
          },
          "tokens_estimate": {
            "type": "integer",
            "description": "Estimated token count of the markdown."
          }
        }
      },
      "ScreenshotRequest": {
        "type": "object",
        "required": [
          "url"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "The URL of the web page to capture."
          },
          "full_page": {
            "type": "boolean",
            "default": false,
            "description": "Capture the entire scrollable page rather than just the viewport."
          },
          "width": {
            "type": "integer",
            "default": 1280,
            "description": "Viewport width in pixels."
          },
          "height": {
            "type": "integer",
            "default": 800,
            "description": "Viewport height in pixels."
          },
          "wait_ms": {
            "type": "integer",
            "default": 0,
            "maximum": 5000,
            "description": "Milliseconds to wait after load before capturing (max 5000)."
          }
        }
      },
      "PdfRequest": {
        "type": "object",
        "required": [
          "url"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "The URL of the web page to render to PDF."
          },
          "width": {
            "type": "integer",
            "description": "Viewport width in pixels."
          },
          "height": {
            "type": "integer",
            "description": "Viewport height in pixels."
          },
          "wait_ms": {
            "type": "integer",
            "description": "Milliseconds to wait after load before rendering."
          }
        }
      },
      "ExtractRequest": {
        "type": "object",
        "required": [
          "schema"
        ],
        "description": "Provide either `url` or `html` (one is required), plus a JSON Schema.",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "The URL of the web page to extract from. Provide this or `html`."
          },
          "html": {
            "type": "string",
            "description": "Raw HTML to extract from. Provide this or `url`."
          },
          "schema": {
            "type": "object",
            "description": "A JSON Schema describing the structure of the data to extract.",
            "additionalProperties": true
          },
          "instructions": {
            "type": "string",
            "description": "Optional natural-language guidance for the extraction."
          },
          "render": {
            "$ref": "#/components/schemas/RenderMode"
          }
        },
        "anyOf": [
          {
            "required": [
              "url"
            ]
          },
          {
            "required": [
              "html"
            ]
          }
        ]
      },
      "ExtractResponse": {
        "type": "object",
        "required": [
          "url",
          "rendered",
          "data"
        ],
        "properties": {
          "url": {
            "type": [
              "string",
              "null"
            ],
            "description": "The source URL, or null when extracting from raw HTML."
          },
          "rendered": {
            "type": "boolean"
          },
          "data": {
            "type": "object",
            "description": "The extracted data, conforming to the supplied JSON Schema.",
            "additionalProperties": true
          }
        }
      },
      "TranscribeRequest": {
        "type": "object",
        "required": [
          "url"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "URL pointing to an audio file to transcribe (max 24MB)."
          }
        }
      },
      "TranscribeSegment": {
        "type": "object",
        "required": [
          "start",
          "end",
          "text"
        ],
        "properties": {
          "start": {
            "type": "number",
            "description": "Segment start time in seconds."
          },
          "end": {
            "type": "number",
            "description": "Segment end time in seconds."
          },
          "text": {
            "type": "string"
          }
        }
      },
      "TranscribeResponse": {
        "type": "object",
        "required": [
          "url",
          "duration_seconds",
          "credits_charged",
          "text",
          "segments",
          "vtt"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          },
          "duration_seconds": {
            "type": [
              "number",
              "null"
            ],
            "description": "Duration of the audio in seconds, if known."
          },
          "credits_charged": {
            "type": "integer",
            "description": "Credits charged: minimum 2, then 1 per started minute."
          },
          "text": {
            "type": "string",
            "description": "The full transcript text."
          },
          "segments": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/TranscribeSegment"
            },
            "description": "Timed transcript segments, or null if unavailable."
          },
          "vtt": {
            "type": [
              "string",
              "null"
            ],
            "description": "WebVTT subtitle track, or null if unavailable."
          }
        }
      },
      "ReviewsRequest": {
        "type": "object",
        "required": [
          "url"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "The URL of the page to scan for schema.org review markup."
          },
          "render": {
            "$ref": "#/components/schemas/RenderMode"
          }
        }
      },
      "AggregateRating": {
        "type": [
          "object",
          "null"
        ],
        "properties": {
          "rating": {
            "type": [
              "number",
              "null"
            ],
            "description": "Aggregate rating value."
          },
          "count": {
            "type": [
              "number",
              "null"
            ],
            "description": "Number of ratings that make up the aggregate."
          }
        }
      },
      "Review": {
        "type": "object",
        "properties": {
          "author": {
            "type": [
              "string",
              "null"
            ]
          },
          "rating": {
            "type": [
              "number",
              "null"
            ]
          },
          "date": {
            "type": [
              "string",
              "null"
            ]
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "body": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "ReviewsResponse": {
        "type": "object",
        "required": [
          "url",
          "rendered",
          "reviews",
          "aggregate"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          },
          "rendered": {
            "type": "boolean"
          },
          "subject": {
            "type": [
              "string",
              "null"
            ],
            "description": "The subject of the reviews (e.g. the product name). Present when review markup is found."
          },
          "aggregate": {
            "$ref": "#/components/schemas/AggregateRating"
          },
          "count": {
            "type": "integer",
            "description": "Number of individual reviews returned."
          },
          "reviews": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Review"
            }
          },
          "message": {
            "type": "string",
            "description": "Present only when no review markup was found. Indicates you were not charged."
          }
        }
      },
      "UsageRecord": {
        "type": "object",
        "required": [
          "endpoint",
          "credits",
          "created_at"
        ],
        "properties": {
          "endpoint": {
            "type": "string"
          },
          "credits": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "description": "ISO 8601 timestamp of the call."
          }
        }
      },
      "UsageResponse": {
        "type": "object",
        "required": [
          "key_id",
          "balance",
          "recent"
        ],
        "properties": {
          "key_id": {
            "type": "string"
          },
          "balance": {
            "type": "integer",
            "description": "Current credit balance."
          },
          "recent": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UsageRecord"
            }
          }
        }
      },
      "HealthResponse": {
        "type": "object",
        "required": [
          "ok"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "The API key is missing or invalid.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "missing_api_key": {
                "summary": "No API key supplied",
                "value": {
                  "error": "missing_api_key",
                  "message": "Provide your API key as 'Authorization: Bearer fk_live_...'."
                }
              },
              "invalid_api_key": {
                "summary": "API key not recognized",
                "value": {
                  "error": "invalid_api_key",
                  "message": "The provided API key is invalid."
                }
              }
            }
          }
        }
      },
      "PaymentRequired": {
        "description": "Insufficient credits to complete this request.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "insufficient_credits",
              "buy": "https://forageapi.com/#pricing"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "rate_limited",
              "message": "Max 60 requests/minute per key."
            }
          }
        }
      },
      "BadGateway": {
        "description": "An upstream fetch or conversion failed. Failed operations are automatically refunded.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "conversion_failed": {
                "value": {
                  "error": "conversion_failed",
                  "message": "The page could not be converted. You were refunded."
                }
              },
              "capture_failed": {
                "value": {
                  "error": "capture_failed",
                  "message": "The page could not be captured. You were refunded."
                }
              },
              "fetch_failed": {
                "value": {
                  "error": "fetch_failed",
                  "message": "The upstream URL could not be fetched. You were refunded."
                }
              }
            }
          }
        }
      }
    }
  }
}
