{
  "openapi": "3.0.3",
  "info": {
    "title": "Keenable API",
    "version": "1.0.0",
    "description": "Web search and page fetching API."
  },
  "servers": [
    {
      "url": "https://api.keenable.ai",
      "description": "Production"
    }
  ],
  "security": [
    {
      "apiKey": []
    },
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/v1/search": {
      "post": {
        "operationId": "search_post",
        "tags": [
          "Search"
        ],
        "summary": "Search the web",
        "description": "Search the web with a JSON request body. Supports date filters, site restriction and search mode.",
        "parameters": [
          {
            "$ref": "#/components/parameters/XKeenableTitleOptional"
          },
          {
            "$ref": "#/components/parameters/XKeenableSessionId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SearchRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Search results",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchResponse"
                }
              }
            }
          },
          "400": {
            "description": "A parameter is missing or invalid, or the body has a parameter search doesn't support. `message` names the parameter and the allowed values. Correct it; retrying unchanged returns the same error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientCredits"
          },
          "403": {
            "description": "Your API key is revoked (`Forbidden`, `Key revoked`): use another key. Also returned when `query` is a URL and Keenable can't fetch it: the site refused access (`Upstream forbidden`) or the URL points to a private host. Search with words instead.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Returned only when `query` is a URL and no page exists there. Check the URL, or search with words.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "422": {
            "description": "Search failed in a way a retry won't fix (`Search failed (<status>)`). Also returned when `query` is a URL whose content Keenable can't extract, for example behind a login or paywall. Change the request; don't retry it unchanged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimitExceeded"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "Returned only when `query` is a URL and Keenable couldn't reach that page (`Bad gateway`). Retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          },
          "504": {
            "description": "Returned only when `query` is a URL and that page took too long to respond (`Gateway timeout`). Retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "529": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/v1/search/public": {
      "post": {
        "operationId": "search_post_public",
        "tags": [
          "Search"
        ],
        "summary": "Search the web without a key",
        "description": "Keyless twin of POST /v1/search. Same request body and same response, no API key. Rate limited per IP address rather than per organization, and consumes no credits. The response has no `usage`.",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/XKeenableTitle"
          },
          {
            "$ref": "#/components/parameters/keenable_title"
          },
          {
            "$ref": "#/components/parameters/XKeenableSessionId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SearchRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Search results",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchResponse"
                }
              }
            }
          },
          "400": {
            "description": "A parameter is missing or invalid, or the `X-Keenable-Title` header is missing (`Missing app identifier`). `message` names the problem. Correct the request; retrying unchanged returns the same error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "`query` is a URL and Keenable can't fetch it: the site refused access (`Upstream forbidden`) or the URL points to a private host. Search with words instead.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Returned only when `query` is a URL and no page exists there. Check the URL, or search with words.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "422": {
            "description": "Search failed in a way a retry won't fix (`Search failed (<status>)`). Also returned when `query` is a URL whose content Keenable can't extract, for example behind a login or paywall. Change the request; don't retry it unchanged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/PublicRateLimitExceeded"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "description": "Returned only when `query` is a URL and Keenable couldn't reach that page (`Bad gateway`). Retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "504": {
            "description": "Returned only when `query` is a URL and that page took too long to respond (`Gateway timeout`). Retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "529": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/v1/fetch": {
      "get": {
        "operationId": "fetch",
        "tags": [
          "Fetch"
        ],
        "summary": "Fetch page content",
        "description": "Read a web page as clean markdown, or only the part a prompt asks for.",
        "parameters": [
          {
            "$ref": "#/components/parameters/url"
          },
          {
            "$ref": "#/components/parameters/max_chars"
          },
          {
            "$ref": "#/components/parameters/live"
          },
          {
            "$ref": "#/components/parameters/prompt"
          },
          {
            "$ref": "#/components/parameters/session_id"
          },
          {
            "$ref": "#/components/parameters/XKeenableTitleOptional"
          },
          {
            "$ref": "#/components/parameters/XKeenableSessionId"
          }
        ],
        "responses": {
          "200": {
            "description": "The page as markdown",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FetchResponse"
                }
              }
            }
          },
          "400": {
            "description": "A parameter is missing or invalid: `url` is missing or isn't an absolute `http` or `https` URL, `max_chars` isn't a positive integer, or `prompt` is over 2000 characters. If your organization has zero data retention, a `prompt` also returns `400` with error `zdr_prompt_unsupported`. Correct the request; retrying unchanged returns the same error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientCredits"
          },
          "403": {
            "description": "Your API key is revoked (`Forbidden`, `Key revoked`): use another key. Also returned when the target site refused the fetch (`Upstream forbidden`), for example a bot wall, or when `url` points to a private or internal host (`Forbidden`). Try a different URL; retrying the same one won't help.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "No page exists at this URL (`Not found`). Check the URL; retrying it returns the same error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "422": {
            "description": "The page was reached but its content couldn't be used: it is behind a login or paywall, the fetch returned a different page, no content could be extracted, or the target returned an error status such as `410` (`The target page returned an error (410)`). Skip the URL; don't retry the same call.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Your rate limit was exceeded, or the target site or the fetcher rate-limited the fetch (those carry `Retry-After: 1`). Wait for `Retry-After`, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          },
          "500": {
            "description": "Keenable had an internal error, including a failure in its own fetcher. Not caused by the URL. Retry up to 3 times with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "502": {
            "description": "Keenable couldn't reach the target page (`Bad gateway`), for example after a DNS, TLS or connection failure, or the target answered with a 5xx status. Often transient: retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          },
          "504": {
            "description": "The target page took too long to respond (`Gateway timeout`). Retry with backoff, or try a different URL.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/fetch/public": {
      "get": {
        "operationId": "fetch_public",
        "tags": [
          "Fetch"
        ],
        "summary": "Fetch page content without a key",
        "description": "Keyless twin of GET /v1/fetch. Same query parameters and same response, no API key. Rate limited per IP address rather than per organization, and consumes no credits. The response has no `usage`.",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/XKeenableTitle"
          },
          {
            "$ref": "#/components/parameters/url"
          },
          {
            "$ref": "#/components/parameters/max_chars"
          },
          {
            "$ref": "#/components/parameters/live"
          },
          {
            "$ref": "#/components/parameters/prompt"
          },
          {
            "$ref": "#/components/parameters/session_id"
          },
          {
            "$ref": "#/components/parameters/keenable_title"
          },
          {
            "$ref": "#/components/parameters/XKeenableSessionId"
          }
        ],
        "responses": {
          "200": {
            "description": "The page as markdown",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FetchResponse"
                }
              }
            }
          },
          "400": {
            "description": "A parameter is missing or invalid, or the `X-Keenable-Title` header is missing (`Missing app identifier`): `url` is missing or isn't an absolute `http` or `https` URL, `max_chars` isn't a positive integer, or `prompt` is over 2000 characters. Correct the request; retrying unchanged returns the same error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "The target site refused the fetch (`Upstream forbidden`), for example a bot wall, or `url` points to a private or internal host (`Forbidden`). Try a different URL; retrying the same one won't help.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "No page exists at this URL (`Not found`). Check the URL; retrying it returns the same error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "422": {
            "description": "The page was reached but its content couldn't be used: it is behind a login or paywall, the fetch returned a different page, no content could be extracted, or the target returned an error status such as `410` (`The target page returned an error (410)`). Skip the URL; don't retry the same call.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Your IP address hit the keyless limit (10 requests per second or 1000 per hour), or the target site or the fetcher rate-limited the fetch. Wait for `Retry-After`, then retry, or use an API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          },
          "500": {
            "description": "Keenable had an internal error, including a failure in its own fetcher. Not caused by the URL. Retry up to 3 times with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "502": {
            "description": "Keenable couldn't reach the target page (`Bad gateway`), for example after a DNS, TLS or connection failure, or the target answered with a 5xx status. Often transient: retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "504": {
            "description": "The target page took too long to respond (`Gateway timeout`). Retry with backoff, or try a different URL.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "Your API key, such as `keen_...`. Create one in the Keenable console. If you send both this header and `Authorization`, the API uses this one."
      },
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Your API key sent as `Authorization: Bearer keen_...`, for clients that only support bearer tokens."
      }
    },
    "parameters": {
      "XKeenableTitle": {
        "name": "X-Keenable-Title",
        "in": "header",
        "required": true,
        "schema": {
          "type": "string",
          "maxLength": 256
        },
        "example": "My App",
        "description": "Name of the application making the call. The public endpoints need it, here or in the `keenable_title` query parameter; without either, the API returns `400` with `Missing app identifier`. Values over 256 characters are truncated."
      },
      "XKeenableTitleOptional": {
        "name": "X-Keenable-Title",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string",
          "maxLength": 256
        },
        "example": "My App",
        "description": "Name of the application making the call. Optional with a key. Values over 256 characters are truncated."
      },
      "keenable_title": {
        "name": "keenable_title",
        "in": "query",
        "required": false,
        "schema": {
          "type": "string",
          "maxLength": 256
        },
        "example": "My App",
        "description": "Same as the `X-Keenable-Title` header, for clients that can't set headers. The header wins if you send both."
      },
      "XKeenableSessionId": {
        "name": "X-Keenable-Session-Id",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string",
          "maxLength": 128
        },
        "description": "Your own ID for grouping related requests. You can send it as the `session_id` parameter instead; the header wins if you send both. Values over 128 characters are truncated."
      },
      "url": {
        "name": "url",
        "in": "query",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uri"
        },
        "example": "https://arxiv.org/abs/2611.00286",
        "description": "The page to read, as an absolute `http` or `https` URL. URL-encode it when it has its own query string."
      },
      "max_chars": {
        "name": "max_chars",
        "in": "query",
        "required": false,
        "schema": {
          "type": "integer",
          "minimum": 1,
          "default": 50000
        },
        "description": "Maximum number of characters of `content`. Longer content is cut and a notice is appended after the cut, so the response runs slightly past this number."
      },
      "live": {
        "name": "live",
        "in": "query",
        "required": false,
        "schema": {
          "type": "boolean",
          "default": false
        },
        "description": "Set to `true` to fetch the page from the source now instead of returning Keenable's indexed copy. Without it, Keenable returns its indexed copy if it has a usable one and fetches the page live otherwise. A call that fetched live is billed as `fetch.live`."
      },
      "prompt": {
        "name": "prompt",
        "in": "query",
        "required": false,
        "schema": {
          "type": "string",
          "maxLength": 2000
        },
        "example": "List all pricing tiers with their monthly prices",
        "description": "An instruction for extracting part of the page, at most 2000 characters. When set, a model reads the page and `content` holds only its answer instead of the whole page."
      },
      "session_id": {
        "name": "session_id",
        "in": "query",
        "required": false,
        "schema": {
          "type": "string",
          "maxLength": 128
        },
        "description": "Your own ID for grouping related requests. Same as the `X-Keenable-Session-Id` header."
      }
    },
    "headers": {
      "RetryAfter": {
        "description": "Seconds to wait before retrying.",
        "schema": {
          "type": "integer"
        },
        "example": 1
      },
      "XRequestId": {
        "description": "ID of this request. Include it when you report a problem.",
        "schema": {
          "type": "string"
        }
      }
    },
    "schemas": {
      "ApiError": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Short error title, such as `Invalid parameter` or `Not found`.",
            "example": "Invalid parameter"
          },
          "message": {
            "type": "string",
            "description": "What went wrong and what to change. Sometimes absent.",
            "example": "\"max_results\": must be an integer between 1 and 50"
          },
          "details": {
            "description": "Structured details, when the error has any."
          },
          "retryAfter": {
            "type": "integer",
            "description": "Seconds to wait before retrying. Present on `429` responses from Keenable's own rate limits, matching the `Retry-After` header."
          }
        }
      },
      "SearchMode": {
        "type": "string",
        "enum": [
          "pro",
          "realtime"
        ],
        "default": "pro",
        "description": "How hard to search: `pro` (default) for deeper retrieval and the best ranking, `realtime` for the fastest response. Each mode is billed as its own SKU (`search.pro`, `search.realtime`). If you leave it out, your organization's default mode applies, which is `pro` unless changed."
      },
      "SearchRequest": {
        "type": "object",
        "required": [
          "query"
        ],
        "properties": {
          "query": {
            "type": "string",
            "example": "post-training quantization LLM",
            "description": "What to search for, in plain language. It must contain search terms; operators such as `site:` alone return `400`, except that a query with only `site:` operators searches for the site name and adds a `warning`. If `query` is a single URL, Keenable fetches that page and returns it as the only result, with a `warning`."
          },
          "mode": {
            "$ref": "#/components/schemas/SearchMode"
          },
          "site": {
            "type": "string",
            "example": "arxiv.org",
            "description": "Return results from one site only. Subdomains are included, and a path narrows further (`github.com/microsoft`)."
          },
          "published_after": {
            "type": "string",
            "example": "2026-01-01",
            "description": "Keep pages published at or after this point. Pages with no known publish date are left out. Takes a date (`YYYY-MM-DD`, which includes that whole day), an ISO 8601 timestamp (UTC if it has no offset), or a relative delta counted back from the request time: `<number><unit>` with units `min`, `h`, `d`, `w` (weeks), `mo` (30 days) or `y` (365 days), such as `7d`."
          },
          "published_before": {
            "type": "string",
            "example": "2026-06-30",
            "description": "Keep pages published at or before this point. A bare `YYYY-MM-DD` includes that whole day; pass a timestamp to cut at an exact instant. Same formats as `published_after`."
          },
          "acquired_after": {
            "type": "string",
            "example": "30d",
            "description": "Keep pages Keenable indexed at or after this point. Same formats as `published_after`."
          },
          "acquired_before": {
            "type": "string",
            "example": "2026-01-01",
            "description": "Keep pages Keenable indexed at or before this point. A bare `YYYY-MM-DD` includes that whole day; pass a timestamp to cut at an exact instant. Same formats as `published_after`."
          },
          "query_time": {
            "type": "string",
            "example": "2026-01-01T00:00:00Z",
            "description": "Search the index as it stood at this moment: pages acquired after it are left out. It sets no lower bound, so it isn't a recency filter. Takes an ISO 8601 timestamp, a `YYYY-MM-DD` date (resolves to `00:00:00` UTC), a Unix epoch, or a relative offset back from now such as `7d` (units `min`, `h`, `d`, `w`, `mo`, `y`). Relative deltas in the date filters still count back from the request time, not from `query_time`."
          },
          "snippet_max_length": {
            "type": "integer",
            "minimum": 180,
            "maximum": 10000,
            "description": "Maximum characters of page text in each result's `snippet`. Without it, snippets can run to several thousand characters."
          },
          "max_results": {
            "type": "integer",
            "minimum": 1,
            "maximum": 50,
            "default": 10,
            "description": "Maximum number of results to return. Fewer come back if fewer pages match."
          },
          "disable_cards": {
            "type": "boolean",
            "default": false,
            "description": "Set to `true` to leave cards (results at `https://cards.keenable.ai/w/<id>` that Keenable writes for queries such as prices or company facts) out of the results; ordinary web pages take their place."
          },
          "session_id": {
            "type": "string",
            "maxLength": 128,
            "description": "Your own ID for grouping related requests. Same as the `X-Keenable-Session-Id` header."
          }
        }
      },
      "SearchResponse": {
        "type": "object",
        "required": [
          "query",
          "mode",
          "results"
        ],
        "properties": {
          "query": {
            "type": "string",
            "description": "The query you sent, unchanged."
          },
          "mode": {
            "type": "string",
            "description": "The mode that served the request, such as `pro` or `realtime`.",
            "example": "pro"
          },
          "results": {
            "type": "array",
            "description": "Matching pages, best match first, at most `max_results` of them.",
            "items": {
              "$ref": "#/components/schemas/SearchResult"
            }
          },
          "warning": {
            "type": "string",
            "description": "Present when the request wasn't answered as a plain search: `query` was a URL, so the page was fetched instead, or `query` had only `site:` operators, so the site name was searched."
          },
          "usage": {
            "$ref": "#/components/schemas/Usage"
          }
        }
      },
      "SearchResult": {
        "type": "object",
        "required": [
          "title",
          "url",
          "description"
        ],
        "properties": {
          "title": {
            "type": "string",
            "description": "Page title. `Untitled` when the page has none.",
            "example": "Quantization of Large Models with Weight Outliers and Sparsity"
          },
          "url": {
            "type": "string",
            "description": "Page URL.",
            "example": "https://arxiv.org/abs/2611.00286"
          },
          "description": {
            "type": "string",
            "description": "Always empty; read `snippet`. Kept for compatibility.",
            "deprecated": true
          },
          "snippet": {
            "type": "string",
            "description": "Text from the page chosen for the query, in markdown. Capped by `snippet_max_length`."
          },
          "published_at": {
            "type": "string",
            "description": "When the page was published, as an ISO 8601 timestamp in UTC. Left out when unknown.",
            "example": "2026-10-30T00:00:00Z"
          },
          "acquired_at": {
            "type": "string",
            "description": "When Keenable indexed the page, as an ISO 8601 timestamp in UTC.",
            "example": "2026-11-03T08:57:22Z"
          }
        }
      },
      "FetchResponse": {
        "type": "object",
        "required": [
          "url",
          "content",
          "description"
        ],
        "properties": {
          "url": {
            "type": "string",
            "description": "The page's final URL after redirects. It can differ from the `url` you sent.",
            "example": "https://arxiv.org/abs/2611.00286"
          },
          "title": {
            "type": "string",
            "description": "Page title. Left out when unknown.",
            "example": "Quantization of Large Models with Weight Outliers and Sparsity"
          },
          "content": {
            "type": "string",
            "description": "The page's main text as markdown, capped by `max_chars`. With `prompt`, the extraction instead of the page."
          },
          "description": {
            "type": "string",
            "description": "Always empty; read `content`. Kept for compatibility.",
            "deprecated": true
          },
          "author": {
            "type": "string",
            "description": "Author name as the page gives it. Left out when unknown."
          },
          "published_at": {
            "type": "integer",
            "format": "int64",
            "description": "When the page was published, as Unix seconds in UTC. Left out when unknown. Search returns `published_at` as an ISO 8601 string instead.",
            "example": 1793318400
          },
          "usage": {
            "$ref": "#/components/schemas/Usage"
          }
        }
      },
      "Usage": {
        "type": "object",
        "required": [
          "sku",
          "amount",
          "credits",
          "paid"
        ],
        "description": "What the call was billed. Present on successful calls made with a key; the public endpoints leave it out.",
        "properties": {
          "sku": {
            "type": "string",
            "description": "The billed operation: `search.pro`, `search.realtime`, `fetch` or `fetch.live`.",
            "example": "search.pro"
          },
          "amount": {
            "type": "integer",
            "description": "How many units of the SKU were billed.",
            "example": 1
          },
          "credits": {
            "type": "number",
            "description": "Credits the call cost."
          },
          "paid": {
            "type": "boolean",
            "description": "`true` if the call drew on purchased credits, `false` if it came from the free allowance."
          }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "No key was sent (`Missing authentication`), or the key is malformed, unknown or has the wrong prefix (`Authentication failed`, with `Malformed API key` or `Invalid API key` in `message`). Send a valid key in `X-API-Key` or `Authorization: Bearer`.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ApiError"
            }
          }
        }
      },
      "InsufficientCredits": {
        "description": "No credits left: this month's free allowance and your purchased credits are used up. Top up in the console, or wait until the free allowance renews. Don't retry.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ApiError"
            }
          }
        }
      },
      "RateLimitExceeded": {
        "description": "Your organization's per-second limit was exceeded, or search or fetch was temporarily rate-limited further down the line (those carry `Retry-After: 1`). Wait for `Retry-After`, then retry.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ApiError"
            }
          }
        },
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        }
      },
      "PublicRateLimitExceeded": {
        "description": "Your IP address hit the keyless limit (10 requests per second or 1000 per hour), or search was temporarily rate-limited further down the line. Wait for `Retry-After`, then retry, or use an API key.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ApiError"
            }
          }
        },
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        }
      },
      "InternalError": {
        "description": "Keenable had an internal error. Retry up to 3 times with backoff.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ApiError"
            }
          }
        }
      },
      "AuthUnavailable": {
        "description": "Keenable couldn't verify your key right now (`Authentication temporarily unavailable`). Wait for `Retry-After` (5 seconds), then retry.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ApiError"
            }
          }
        },
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        }
      },
      "Overloaded": {
        "description": "Search is temporarily overloaded (`Overloaded`). Wait for `Retry-After` (1 second), then retry.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ApiError"
            }
          }
        },
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        }
      }
    }
  }
}
