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.


    Authentication: `Authorization: Bearer <api_key>`.


    **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.


    Up 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[]`.


        A 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: &a1
              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: &a3
              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: &a4
              description: URLs you can send right now.
              schema:
                type: integer
            Idempotency-Key: &a2
              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

                      …
                    text: |-
                      Article

                      …
                    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: *a1
            Retry-After: &a5
              description: "Seconds to wait: before retrying a 429, or before repeating a 202
                with the same Idempotency-Key."
              schema:
                type: integer
            Idempotency-Key: *a2
          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: *a1
            Idempotency-Key: *a2
          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": &a6
          description: "UNAUTHORIZED: Missing or invalid API key."
          headers:
            X-Request-Id: *a1
            Idempotency-Key: *a2
          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: *a1
            Idempotency-Key: *a2
          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": &a7
          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: *a1
            Idempotency-Key: *a2
          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: *a1
            Idempotency-Key: *a2
          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: *a1
          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: *a1
            X-RateLimit-Limit: *a3
            X-RateLimit-Remaining: *a4
            Retry-After: *a5
            Idempotency-Key: *a2
          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: *a1
            Idempotency-Key: *a2
          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: *a1
            Idempotency-Key: *a2
          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": *a6
        "403": *a7
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
