{
  "openapi": "3.1.0",
  "info": {
    "title": "Live Web Data API",
    "version": "1.0.0",
    "summary": "Live web pages as clean Markdown, text or HTML for AI agents.",
    "description": "Fetch public web pages through our fetch network and get clean Markdown, text or HTML back.\n\nAuthentication: `Authorization: Bearer <api_key>`.\n\n**Per-URL errors** (HTTP 200, in `statuses[].error.tag`, 0 credits): `CRAWL_NOT_FOUND`, `SOURCE_NOT_AVAILABLE`, `CRAWL_LIVECRAWL_TIMEOUT`, `CRAWL_NOT_RENDERED`, `UNSUPPORTED_CONTENT_TYPE`, `CRAWL_UNKNOWN_ERROR`, `SOURCE_BLOCKED`, `BROWSER_UNAVAILABLE`, `RATE_LIMITED`, and `RESTRICTED_TARGET` for blocked URLs inside a multi-URL request.\n\nUp to 10 URLs per request; rate limits count URLs.",
    "contact": {
      "email": "support@livewebdata.com"
    }
  },
  "servers": [
    {
      "url": "https://livewebdata.com/api",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Contents"
    },
    {
      "name": "Account"
    }
  ],
  "paths": {
    "/v1/contents": {
      "post": {
        "operationId": "getContents",
        "tags": [
          "Contents"
        ],
        "summary": "Fetch page contents",
        "description": "Fetch 1–10 URLs live and return clean content. HTTP 200 even if individual URLs failed — check `statuses[]`.\n\nA fetch still running after about 100 seconds answers **202** instead: send the same request again with the `Idempotency-Key` from that answer (after `Retry-After` seconds) to get the result. A repeat is not charged again. Results are kept for 15 minutes; a result nobody collects stays charged. Always send your own `Idempotency-Key`: a connection cut after the API received the request (a network error, a timeout, a deploy) can then be retried with the same key without a second charge.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Your id for this request, 8–128 characters of `A–Z a–z 0–9 _ - : .`. A repeat with the same key and the same body returns the same result instead of fetching (and charging) again, for 24 hours; with a different body it answers 409. Without it, the API creates one and returns it in the `Idempotency-Key` response header.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_\\-:.]{8,128}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContentsRequest"
              },
              "examples": {
                "single": {
                  "summary": "One URL",
                  "value": {
                    "urls": [
                      "https://example.com/article"
                    ]
                  }
                },
                "options": {
                  "summary": "All options",
                  "value": {
                    "urls": [
                      "https://example.com/article"
                    ],
                    "countryCode": "de",
                    "text": {
                      "maxCharacters": 50000,
                      "verbosity": "compact"
                    },
                    "livecrawlTimeout": 15000
                  }
                },
                "multi": {
                  "summary": "Several URLs (one restricted)",
                  "value": {
                    "urls": [
                      "https://example.com/a",
                      "https://example.com/b",
                      "https://example.com/blocked"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Processed. One status per URL; successful pages in results.",
            "headers": {
              "X-Request-Id": {
                "description": "Same as requestId in the body.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Credits-Remaining": {
                "description": "Credit balance after this request.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "description": "URLs you can send at once (bucket size, = rateLimit.burst in GET /v1/account). The bucket refills at rateLimit.requestsPerSecond URLs per second.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "URLs you can send right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "Idempotency-Key": {
                "description": "The request's Idempotency-Key: yours, or one the API created when you sent none. On every answer (200, 202, errors) except a 400 for a malformed key and a 413 for a body over 10 MiB (refused unread). Repeat a 202 with it.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContentsResponse"
                },
                "example": {
                  "requestId": "req_01JEXAMPLE0000000000000",
                  "results": [
                    {
                      "id": "https://example.com/article",
                      "url": "https://example.com/article",
                      "title": "Article",
                      "author": null,
                      "publishedDate": null,
                      "image": null,
                      "favicon": "https://example.com/favicon.ico",
                      "markdown": "# Article\n\n…",
                      "text": "Article\n\n…",
                      "html": "<!doctype html>…"
                    }
                  ],
                  "statuses": [
                    {
                      "id": "https://example.com/article",
                      "status": "success",
                      "source": "livecrawled",
                      "httpStatusCode": 200,
                      "strategy": "fast",
                      "credits": 1,
                      "error": null
                    }
                  ],
                  "searchTime": 1240
                }
              }
            }
          },
          "202": {
            "description": "Still fetching after about 100 seconds. Send the same request again with this Idempotency-Key after Retry-After seconds; the repeat waits for the result and is not charged again.",
            "headers": {
              "X-Request-Id": {
                "description": "Same as requestId in the body.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait: before retrying a 429, or before repeating a 202 with the same Idempotency-Key.",
                "schema": {
                  "type": "integer"
                }
              },
              "Idempotency-Key": {
                "description": "The request's Idempotency-Key: yours, or one the API created when you sent none. On every answer (200, 202, errors) except a 400 for a malformed key and a 413 for a body over 10 MiB (refused unread). Repeat a 202 with it.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Pending"
                },
                "example": {
                  "status": "pending",
                  "requestId": "req_01JEXAMPLE0000000000000",
                  "idempotencyKey": "lwda_3f1c2b7e-0d4a-4c1e-9a55-2b8f0e6c7d10",
                  "retryAfter": 1,
                  "message": "Still fetching. Send the same request again with the same Idempotency-Key header to get the result; it is not charged again."
                }
              }
            }
          },
          "400": {
            "description": "INVALID_REQUEST: Invalid parameters. TOO_MANY_URLS: More URLs than allowed per request (10). FREE_TIER_SINGLE_URL: Free test: one URL per request. Upgrade for up to 10.",
            "headers": {
              "X-Request-Id": {
                "description": "Same as requestId in the body.",
                "schema": {
                  "type": "string"
                }
              },
              "Idempotency-Key": {
                "description": "The request's Idempotency-Key: yours, or one the API created when you sent none. On every answer (200, 202, errors) except a 400 for a malformed key and a 413 for a body over 10 MiB (refused unread). Repeat a 202 with it.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "INVALID_REQUEST": {
                    "value": {
                      "error": {
                        "code": 400,
                        "tag": "INVALID_REQUEST",
                        "message": "Invalid parameters."
                      },
                      "requestId": "req_01JEXAMPLE0000000000000"
                    }
                  },
                  "TOO_MANY_URLS": {
                    "value": {
                      "error": {
                        "code": 400,
                        "tag": "TOO_MANY_URLS",
                        "message": "More URLs than allowed per request (10)."
                      },
                      "requestId": "req_01JEXAMPLE0000000000000"
                    }
                  },
                  "FREE_TIER_SINGLE_URL": {
                    "value": {
                      "error": {
                        "code": 400,
                        "tag": "FREE_TIER_SINGLE_URL",
                        "message": "Free test: one URL per request. Upgrade for up to 10."
                      },
                      "requestId": "req_01JEXAMPLE0000000000000"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHORIZED: Missing or invalid API key.",
            "headers": {
              "X-Request-Id": {
                "description": "Same as requestId in the body.",
                "schema": {
                  "type": "string"
                }
              },
              "Idempotency-Key": {
                "description": "The request's Idempotency-Key: yours, or one the API created when you sent none. On every answer (200, 202, errors) except a 400 for a malformed key and a 413 for a body over 10 MiB (refused unread). Repeat a 202 with it.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "UNAUTHORIZED": {
                    "value": {
                      "error": {
                        "code": 401,
                        "tag": "UNAUTHORIZED",
                        "message": "Missing or invalid API key."
                      },
                      "requestId": "req_01JEXAMPLE0000000000000"
                    }
                  }
                }
              }
            }
          },
          "402": {
            "description": "INSUFFICIENT_CREDITS: Not enough credits. Top up or upgrade. On the free test the body adds nextResetAt and freeTierEnded.",
            "headers": {
              "X-Request-Id": {
                "description": "Same as requestId in the body.",
                "schema": {
                  "type": "string"
                }
              },
              "Idempotency-Key": {
                "description": "The request's Idempotency-Key: yours, or one the API created when you sent none. On every answer (200, 202, errors) except a 400 for a malformed key and a 413 for a body over 10 MiB (refused unread). Repeat a 202 with it.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "INSUFFICIENT_CREDITS": {
                    "value": {
                      "error": {
                        "code": 402,
                        "tag": "INSUFFICIENT_CREDITS",
                        "message": "Not enough credits. Top up or upgrade. On the free test the body adds nextResetAt and freeTierEnded."
                      },
                      "requestId": "req_01JEXAMPLE0000000000000"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "KEY_SUSPENDED: The key or account is suspended or not yet approved. FREE_TIER_FEATURE: Free test: this feature is available on paid plans.",
            "headers": {
              "X-Request-Id": {
                "description": "Same as requestId in the body.",
                "schema": {
                  "type": "string"
                }
              },
              "Idempotency-Key": {
                "description": "The request's Idempotency-Key: yours, or one the API created when you sent none. On every answer (200, 202, errors) except a 400 for a malformed key and a 413 for a body over 10 MiB (refused unread). Repeat a 202 with it.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "KEY_SUSPENDED": {
                    "value": {
                      "error": {
                        "code": 403,
                        "tag": "KEY_SUSPENDED",
                        "message": "The key or account is suspended or not yet approved."
                      },
                      "requestId": "req_01JEXAMPLE0000000000000"
                    }
                  },
                  "FREE_TIER_FEATURE": {
                    "value": {
                      "error": {
                        "code": 403,
                        "tag": "FREE_TIER_FEATURE",
                        "message": "Free test: this feature is available on paid plans."
                      },
                      "requestId": "req_01JEXAMPLE0000000000000"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "IDEMPOTENCY_CONFLICT: The Idempotency-Key was already used with a different request, or its result is no longer available (results are kept 15 minutes). Use a new key.",
            "headers": {
              "X-Request-Id": {
                "description": "Same as requestId in the body.",
                "schema": {
                  "type": "string"
                }
              },
              "Idempotency-Key": {
                "description": "The request's Idempotency-Key: yours, or one the API created when you sent none. On every answer (200, 202, errors) except a 400 for a malformed key and a 413 for a body over 10 MiB (refused unread). Repeat a 202 with it.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "IDEMPOTENCY_CONFLICT": {
                    "value": {
                      "error": {
                        "code": 409,
                        "tag": "IDEMPOTENCY_CONFLICT",
                        "message": "The Idempotency-Key was already used with a different request, or its result is no longer available (results are kept 15 minutes). Use a new key."
                      },
                      "requestId": "req_01JEXAMPLE0000000000000"
                    }
                  }
                }
              }
            }
          },
          "413": {
            "description": "PAYLOAD_TOO_LARGE: The request body is larger than 10 MiB. Nothing was read or charged.",
            "headers": {
              "X-Request-Id": {
                "description": "Same as requestId in the body.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "PAYLOAD_TOO_LARGE": {
                    "value": {
                      "error": {
                        "code": 413,
                        "tag": "PAYLOAD_TOO_LARGE",
                        "message": "The request body is larger than 10 MiB. Nothing was read or charged."
                      },
                      "requestId": "req_01JEXAMPLE0000000000000"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED: Rate limit exceeded. Retry after the Retry-After header.",
            "headers": {
              "X-Request-Id": {
                "description": "Same as requestId in the body.",
                "schema": {
                  "type": "string"
                }
              },
              "X-RateLimit-Limit": {
                "description": "URLs you can send at once (bucket size, = rateLimit.burst in GET /v1/account). The bucket refills at rateLimit.requestsPerSecond URLs per second.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "URLs you can send right now.",
                "schema": {
                  "type": "integer"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait: before retrying a 429, or before repeating a 202 with the same Idempotency-Key.",
                "schema": {
                  "type": "integer"
                }
              },
              "Idempotency-Key": {
                "description": "The request's Idempotency-Key: yours, or one the API created when you sent none. On every answer (200, 202, errors) except a 400 for a malformed key and a 413 for a body over 10 MiB (refused unread). Repeat a 202 with it.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "RATE_LIMITED": {
                    "value": {
                      "error": {
                        "code": 429,
                        "tag": "RATE_LIMITED",
                        "message": "Rate limit exceeded. Retry after the Retry-After header."
                      },
                      "requestId": "req_01JEXAMPLE0000000000000"
                    }
                  }
                }
              }
            }
          },
          "451": {
            "description": "RESTRICTED_TARGET: All requested URLs are restricted. No credits charged.",
            "headers": {
              "X-Request-Id": {
                "description": "Same as requestId in the body.",
                "schema": {
                  "type": "string"
                }
              },
              "Idempotency-Key": {
                "description": "The request's Idempotency-Key: yours, or one the API created when you sent none. On every answer (200, 202, errors) except a 400 for a malformed key and a 413 for a body over 10 MiB (refused unread). Repeat a 202 with it.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "RESTRICTED_TARGET": {
                    "value": {
                      "error": {
                        "code": 451,
                        "tag": "RESTRICTED_TARGET",
                        "message": "All requested URLs are restricted. No credits charged."
                      },
                      "requestId": "req_01JEXAMPLE0000000000000"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "SERVICE_UNAVAILABLE: Temporarily unavailable. Retry shortly.",
            "headers": {
              "X-Request-Id": {
                "description": "Same as requestId in the body.",
                "schema": {
                  "type": "string"
                }
              },
              "Idempotency-Key": {
                "description": "The request's Idempotency-Key: yours, or one the API created when you sent none. On every answer (200, 202, errors) except a 400 for a malformed key and a 413 for a body over 10 MiB (refused unread). Repeat a 202 with it.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "SERVICE_UNAVAILABLE": {
                    "value": {
                      "error": {
                        "code": 503,
                        "tag": "SERVICE_UNAVAILABLE",
                        "message": "Temporarily unavailable. Retry shortly."
                      },
                      "requestId": "req_01JEXAMPLE0000000000000"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/account": {
      "get": {
        "operationId": "getAccount",
        "tags": [
          "Account"
        ],
        "summary": "Get account status",
        "description": "Plan, remaining credits and rate limit for the calling key. Free. Use before large batches.",
        "responses": {
          "200": {
            "description": "Account summary.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Account"
                },
                "example": {
                  "plan": "starter",
                  "status": "active",
                  "credits": {
                    "plan": 81234,
                    "topup": 10000,
                    "total": 91234
                  },
                  "rateLimit": {
                    "requestsPerSecond": 2,
                    "burst": 4
                  },
                  "periodEndsAt": "2026-11-01T00:00:00Z"
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHORIZED: Missing or invalid API key.",
            "headers": {
              "X-Request-Id": {
                "description": "Same as requestId in the body.",
                "schema": {
                  "type": "string"
                }
              },
              "Idempotency-Key": {
                "description": "The request's Idempotency-Key: yours, or one the API created when you sent none. On every answer (200, 202, errors) except a 400 for a malformed key and a 413 for a body over 10 MiB (refused unread). Repeat a 202 with it.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "UNAUTHORIZED": {
                    "value": {
                      "error": {
                        "code": 401,
                        "tag": "UNAUTHORIZED",
                        "message": "Missing or invalid API key."
                      },
                      "requestId": "req_01JEXAMPLE0000000000000"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "KEY_SUSPENDED: The key or account is suspended or not yet approved. FREE_TIER_FEATURE: Free test: this feature is available on paid plans.",
            "headers": {
              "X-Request-Id": {
                "description": "Same as requestId in the body.",
                "schema": {
                  "type": "string"
                }
              },
              "Idempotency-Key": {
                "description": "The request's Idempotency-Key: yours, or one the API created when you sent none. On every answer (200, 202, errors) except a 400 for a malformed key and a 413 for a body over 10 MiB (refused unread). Repeat a 202 with it.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "KEY_SUSPENDED": {
                    "value": {
                      "error": {
                        "code": 403,
                        "tag": "KEY_SUSPENDED",
                        "message": "The key or account is suspended or not yet approved."
                      },
                      "requestId": "req_01JEXAMPLE0000000000000"
                    }
                  },
                  "FREE_TIER_FEATURE": {
                    "value": {
                      "error": {
                        "code": 403,
                        "tag": "FREE_TIER_FEATURE",
                        "message": "Free test: this feature is available on paid plans."
                      },
                      "requestId": "req_01JEXAMPLE0000000000000"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "API key, e.g. 1337_live_…"
      }
    },
    "schemas": {
      "ContentsRequest": {
        "type": "object",
        "properties": {
          "urls": {
            "minItems": 1,
            "maxItems": 10,
            "type": "array",
            "items": {
              "type": "string",
              "maxLength": 8192,
              "format": "uri"
            },
            "description": "Pages to fetch (1–10), each at most 8,192 characters. Processed in parallel; one entry per URL in statuses. Duplicates (same URL, ignoring #fragment) are fetched once; repeated positions cost 0 credits."
          },
          "countryCode": {
            "description": "ISO-3166-1 alpha-2 country to fetch from. Default \"us\".",
            "type": "string",
            "minLength": 2,
            "maxLength": 2
          },
          "text": {
            "description": "Content options. Default enabled.",
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "object",
                "properties": {
                  "maxCharacters": {
                    "description": "Truncate text and markdown to this many characters. Default 100000. results[].textTruncated tells you when it did.",
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 1000000
                  },
                  "verbosity": {
                    "description": "\"compact\" = main content (default), \"full\" = whole body.",
                    "type": "string",
                    "enum": [
                      "compact",
                      "full"
                    ]
                  }
                },
                "additionalProperties": false
              }
            ]
          },
          "maxAgeHours": {
            "description": "Accepted only as 0 (for older clients): there is no cache, every request is fetched live.",
            "type": "number",
            "const": 0
          },
          "html": {
            "description": "Include the page HTML as served in results[].html. Default false. A response carries at most 24 MiB of JSON: past that, the remaining results come without html (warning RESPONSE_SIZE_LIMIT).",
            "type": "boolean"
          },
          "livecrawlTimeout": {
            "description": "Live fetch timeout in ms (1–90000). Default 30000, and a lower value is raised to 30000 (the browser fallback needs it); with renderFallback: false any value, default 10000; with render or renderFallback: true at least 30000, default 60000.",
            "type": "integer",
            "minimum": 1,
            "maximum": 90000
          },
          "render": {
            "description": "Render every URL in a real browser, as a normal visitor sees it (3 credits, 5 when the page needed a retry through another connection). Cannot be combined with renderFallback. Default false.",
            "type": "boolean"
          },
          "renderFallback": {
            "description": "Fetch normally first; render in a real browser only when the first attempt did not return the page, shows no or very little visible text, or was blocked. On by default (omit it); false = plain fetch only, never a browser. A normal success costs its normal credits, a rendered one only the render credits. Cannot be combined with render: true.",
            "type": "boolean"
          }
        },
        "required": [
          "urls"
        ],
        "additionalProperties": false
      },
      "ContentsResponse": {
        "type": "object",
        "properties": {
          "requestId": {
            "type": "string"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ContentsResult"
            },
            "description": "One entry per successfully fetched URL."
          },
          "statuses": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ContentsStatus"
            },
            "description": "One entry per requested URL."
          },
          "searchTime": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991,
            "description": "Processing time in ms."
          }
        },
        "required": [
          "requestId",
          "results",
          "statuses",
          "searchTime"
        ]
      },
      "ContentsResult": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The requested URL (matches statuses[].id)."
          },
          "url": {
            "type": "string",
            "description": "Final URL after redirects."
          },
          "title": {
            "description": "At most 2,000 characters (cut with an ellipsis).",
            "type": [
              "string",
              "null"
            ]
          },
          "author": {
            "description": "At most 2,000 characters (cut with an ellipsis).",
            "type": [
              "string",
              "null"
            ]
          },
          "publishedDate": {
            "type": [
              "string",
              "null"
            ]
          },
          "image": {
            "description": "null when the page names none, or a URL longer than 4,096 characters.",
            "type": [
              "string",
              "null"
            ]
          },
          "favicon": {
            "description": "The page's icon, else /favicon.ico of its origin (an icon URL longer than 4,096 characters is not used).",
            "type": [
              "string",
              "null"
            ]
          },
          "text": {
            "description": "Plain text, tags removed, whitespace normalized.",
            "type": "string"
          },
          "markdown": {
            "description": "Main content as Markdown.",
            "type": "string"
          },
          "html": {
            "description": "Page HTML as served. Only with html: true, and only while the response stays within its 24 MiB size limit (else warning RESPONSE_SIZE_LIMIT).",
            "type": "string"
          },
          "textTruncated": {
            "description": "true when text or markdown was cut at text.maxCharacters, or at the response size limit (warning RESPONSE_SIZE_LIMIT). Present whenever text is enabled.",
            "type": "boolean"
          },
          "warnings": {
            "description": "Absent when empty. \"PAGE_TRUNCATED\" = only part of the page could be retrieved; \"THIN_CONTENT\" = the page probably needs render: true; \"EXTRACTION_FALLBACK\" = converted as plain text (too complex in time); \"RESPONSE_SIZE_LIMIT\" = the response reached its 24 MiB size limit, so this result has no html (and maybe cut text/markdown).",
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string",
                  "enum": [
                    "PAGE_TRUNCATED",
                    "THIN_CONTENT",
                    "EXTRACTION_FALLBACK",
                    "RESPONSE_SIZE_LIMIT"
                  ]
                },
                "message": {
                  "type": "string"
                }
              },
              "required": [
                "code",
                "message"
              ]
            }
          }
        },
        "required": [
          "id",
          "url",
          "title",
          "author",
          "publishedDate",
          "image",
          "favicon"
        ]
      },
      "ContentsStatus": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "success",
              "error"
            ]
          },
          "source": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "livecrawled",
                  "cached",
                  "cached-fallback"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "Always \"livecrawled\" on success (there is no cache); null on error."
          },
          "fetchedAt": {
            "description": "When the page was fetched (ISO 8601); null on error.",
            "type": [
              "string",
              "null"
            ]
          },
          "httpStatusCode": {
            "anyOf": [
              {
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991
              },
              {
                "type": "null"
              }
            ]
          },
          "strategy": {
            "type": "string",
            "enum": [
              "fast",
              "retry",
              "browser",
              "local-browser"
            ],
            "description": "\"fast\" = standard fetch (1 credit). \"retry\" = the first attempt did not return the page, a retry through another connection did (2 credits). \"browser\" = rendered in a real browser, as a normal visitor sees it (3 credits, 5 when it needed a retry through another connection). \"local-browser\" is reserved."
          },
          "credits": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991,
            "description": "Credits charged for this URL: 1 standard, 2 retried, 3 rendered, 5 rendered + retried, 0 on error and for a short page delivered with thin: true (a success, not an error)."
          },
          "fallbackUsed": {
            "description": "Present (true) only when the browser fallback ran for this URL: with strategy \"browser\" the content is that render (fast → render); with another strategy, or on error, rendering was tried and did not help.",
            "type": "boolean"
          },
          "thin": {
            "description": "Present (true) only for a short page delivered at 0 credits: fewer than 500 characters of visible text even after rendering. The result holds only that visible text. A success, not an error.",
            "type": "boolean"
          },
          "error": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "tag": {
                    "type": "string",
                    "enum": [
                      "CRAWL_NOT_FOUND",
                      "SOURCE_NOT_AVAILABLE",
                      "CRAWL_LIVECRAWL_TIMEOUT",
                      "CRAWL_NOT_RENDERED",
                      "UNSUPPORTED_CONTENT_TYPE",
                      "CRAWL_UNKNOWN_ERROR",
                      "SOURCE_BLOCKED",
                      "BROWSER_UNAVAILABLE",
                      "RATE_LIMITED",
                      "RESTRICTED_TARGET"
                    ]
                  },
                  "message": {
                    "type": "string"
                  }
                },
                "required": [
                  "tag",
                  "message"
                ]
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "id",
          "status",
          "source",
          "fetchedAt",
          "httpStatusCode",
          "strategy",
          "credits",
          "error"
        ]
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991
              },
              "tag": {
                "type": "string",
                "enum": [
                  "INVALID_REQUEST",
                  "TOO_MANY_URLS",
                  "UNAUTHORIZED",
                  "INSUFFICIENT_CREDITS",
                  "KEY_SUSPENDED",
                  "RATE_LIMITED",
                  "RESTRICTED_TARGET",
                  "IDEMPOTENCY_CONFLICT",
                  "PAYLOAD_TOO_LARGE",
                  "SERVICE_UNAVAILABLE",
                  "FREE_TIER_SINGLE_URL",
                  "FREE_TIER_FEATURE"
                ]
              },
              "message": {
                "type": "string"
              },
              "nextResetAt": {
                "description": "Free test, 402 only: when this week's credits come back (Monday 00:00 UTC).",
                "type": [
                  "string",
                  "null"
                ]
              },
              "freeTierEnded": {
                "description": "Free test, 402 only: true once the 4 weeks are over.",
                "type": "boolean"
              },
              "upgradeUrl": {
                "description": "Free test, 402 only: where to choose a plan.",
                "type": "string"
              }
            },
            "required": [
              "code",
              "tag",
              "message"
            ]
          },
          "requestId": {
            "type": "string"
          }
        },
        "required": [
          "error",
          "requestId"
        ]
      },
      "Pending": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "const": "pending"
          },
          "requestId": {
            "type": "string",
            "description": "Same for every repeat with the same Idempotency-Key."
          },
          "idempotencyKey": {
            "type": "string",
            "description": "Send the same request again with this Idempotency-Key header."
          },
          "retryAfter": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991,
            "description": "Seconds to wait before repeating (also the Retry-After header)."
          },
          "message": {
            "type": "string"
          }
        },
        "required": [
          "status",
          "requestId",
          "idempotencyKey",
          "retryAfter",
          "message"
        ]
      },
      "Account": {
        "type": "object",
        "properties": {
          "plan": {
            "type": "string",
            "description": "Plan id, e.g. \"starter\" (\"trial\" before a plan is activated)."
          },
          "status": {
            "type": "string",
            "description": "Account status, e.g. active, pending_review, suspended."
          },
          "credits": {
            "type": "object",
            "properties": {
              "plan": {
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991
              },
              "topup": {
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991
              },
              "total": {
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991
              }
            },
            "required": [
              "plan",
              "topup",
              "total"
            ]
          },
          "rateLimit": {
            "type": "object",
            "properties": {
              "requestsPerSecond": {
                "type": "number",
                "description": "Refill rate: URLs per second, sustained."
              },
              "burst": {
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991,
                "description": "Bucket size: URLs you can send at once. Same number as the X-RateLimit-Limit header."
              }
            },
            "required": [
              "requestsPerSecond",
              "burst"
            ]
          },
          "periodEndsAt": {
            "description": "End of the current billing period (ISO 8601).",
            "type": [
              "string",
              "null"
            ]
          },
          "freeTest": {
            "description": "Free test only (plan \"free\").",
            "type": "object",
            "properties": {
              "eligible": {
                "type": "boolean",
                "description": "false: this sign-up was refused free credits; choose a paid plan to continue."
              },
              "week": {
                "anyOf": [
                  {
                    "type": "integer",
                    "minimum": -9007199254740991,
                    "maximum": 9007199254740991
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Current free week, 1–4; null after the end or when not eligible."
              },
              "weeks": {
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991,
                "description": "Length of the free test in weeks (4)."
              },
              "creditsPerWeek": {
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991,
                "description": "This week's grant: 100 in week 1, then 50. Credits left = credits.plan."
              },
              "nextResetAt": {
                "description": "Next weekly grant, Monday 00:00 UTC; null in the last week, after the end or when not eligible.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "endsAt": {
                "description": "End of the free test (after the 4th week).",
                "type": [
                  "string",
                  "null"
                ]
              },
              "ended": {
                "type": "boolean"
              }
            },
            "required": [
              "eligible",
              "week",
              "weeks",
              "creditsPerWeek",
              "nextResetAt",
              "endsAt",
              "ended"
            ]
          }
        },
        "required": [
          "plan",
          "status",
          "credits",
          "rateLimit",
          "periodEndsAt"
        ]
      }
    }
  }
}
