---
title: "Read a page as Markdown, with or without a browser"
method: POST
path: "/scrape"
tags: ["Capture"]
---

# Read a page as Markdown, with or without a browser

`POST /scrape`

## Request

- Base URL: `https://api.domscout.io`
- URL: `https://api.domscout.io/scrape`
- Auth: API key in header `x-api-key`

The same capture engine POST /screenshot runs, addressed as a content endpoint. It defaults extractMarkdown:true and forces responseType:json, and it drops the base64 image from the response unless screenshot:true asks for it — which is what keeps a text answer inside the synchronous response budget on pages whose PNG would not fit.

Every field, plan gate, entitlement, credit weight and error contract is POST /screenshot's. The only differences are the three defaults above and the response shape.

Send renderJs:false (or fast:true) to answer from one guarded HTTP GET instead of a browser. That path is roughly an order of magnitude faster, costs the same credits, and returns the identical document shape — but it does not execute JavaScript, so check metadata.renderJs and metadata.contentQuality before concluding a page is empty. renderJs:false is also accepted on POST /screenshot, where it returns this document shape because there is no image to return.

## Headers

- `Idempotency-Key` string

## Request body

- union: Existing screenshot options are retained. New inspection, rendering, cleanup, diagnostics, extraction, retention, and action fields are additive.
  - object
    - `url` string, uri: Public HTTP(S) page URL. Unsafe addresses and redirects are rejected. Credentials in the authority (https://user:pass@host/) are refused with 400 — send authentication in the headers field instead, so it is never written to a capture log.
    - `html` string: Raw HTML to render instead of navigating to a URL.
    - `format` 'png' | 'jpeg' | 'webp' | 'pdf'
    - `responseType` 'json' | 'binary': How the capture comes back. "json" (default) returns a JSON envelope with base64 image data plus metadata and any analysis you requested. "binary" returns the raw file bytes with a matching Content-Type, and therefore has nowhere to carry metadata — so a binary request combined with an analysis option (cleanup, semanticSnapshot, diagnostics, or extract) is AUTO-PROMOTED to a durable job: it answers 202 with a jobId and metadata.autoPromotedAsync, and you poll GET /job/{jobId} for the result. A capture that fails after promotion reports its failure on the job rather than as a synchronous 4xx, carrying the same error code it would have returned inline. Binary responses also cannot carry metadata.ignoredFields.
    - `async` boolean: Force durable asynchronous execution. The service also auto-promotes captures expected to be large (rich JSON output) or slow (a URL capture stacking two or more whole-DOM passes, or one combined with fullPage/lazyScroll). Slow-capture promotion applies to Pro and above; Hobby keeps inline results.
    - `probe` boolean: Load and measure the page without a screenshot.
    - `width` integer
    - `height` integer
    - `isMobile` boolean
    - `hasTouch` boolean
    - `deviceScaleFactor` number
    - `quality` integer
    - `fullPage` boolean: Capture the whole scrollable page rather than the viewport. Adds a credit surcharge. The rendered page may be at most 12000px tall and 3840px wide; beyond that the request is refused with 400 and code CAPTURE_TOO_TALL or CAPTURE_TOO_WIDE rather than a truncated image. The same ceiling applies to captureSelector.
    - `selector` string, required: Legacy alias for captureSelector.
    - `captureSelector` string: The matched element must be visible within 5000ms, or the request is refused with 400 and code CAPTURE_SELECTOR_NOT_FOUND. It is also subject to the same 12000px ceiling as fullPage (400, CAPTURE_TOO_TALL).
    - `hideSelectors` string[]
    - `captureMode` 'default' | 'grid' | 'skeleton': default uses the requested viewport. grid produces responsive desktop, tablet, and mobile frames without reloading raw inline HTML. skeleton preserves a DOMContentLoaded frame before waiting for bounded hydration, so an inline HTML skeleton is still useful even if later network-idle settling times out. grid and skeleton also honour extractMarkdown; the Markdown is taken from the DOM after the mode has finished capturing, so for grid that is the final (desktop) viewport.
    - `waitForSelector` string: A wait that expires is a client-side outcome, not a server fault: the request is refused with 400 and code WAIT_FOR_SELECTOR_TIMEOUT.
    - `delay` integer
    - `headers` object: Scoped only to requests to the original target origin, including opened tabs and redirects. Sensitive header values do not appear in diagnostics or persisted artifacts.
    - `cookies` Cookie[]
      - `selector` string
      - `name` string, required
      - `value` string, required
      - `domain` string
      - `path` string
      - `httpOnly` boolean
      - `secure` boolean
      - `sameSite` 'Strict' | 'Lax' | 'None'
    - `injectCSS` string
    - `injectJS` string: Trusted-JavaScript feature. It requires an eligible plan, an operator-controlled account entitlement, and the platform-wide emergency switch.
    - `aiMode` boolean
    - `debugOverlay` boolean: Render console errors, page exceptions, failed requests and oversized assets into an overlay on the captured image. At most 200 entries are collected and each is truncated to 2048 characters; past that the overlay ends with a [TRUNCATED] marker. For structured, machine-readable output use `diagnostics` instead.
    - `performanceOverlay` boolean
    - `forceState` ForceState[]
      - `selector` string, required: A CSS selector. If it matches several elements the state is applied to the first, and the result says so.
      - `state` 'hover' | 'focus', required
    - `actions` Action[]
      - `action` 'click' | 'type' | 'scroll' | 'wait' | 'navigate' | 'back' | 'forward' | 'reload' | 'select' | 'check' | 'uncheck' | 'hover' | 'focus' | 'pressKey' | 'scrollIntoView' | 'waitForText' | 'waitForUrl' | 'waitForFunction' | 'waitForRequest' | 'waitForResponse' | 'waitForNetworkIdle' | 'openTab' | 'switchTab' | 'closeTab' | 'assert' | 'upload', required
      - `target` union: A selector-based action target. Semantic snapshot node IDs are inspection evidence only and are intentionally not accepted as durable action targets.
        - object
          - `css` string, required
        - object
          - `xpath` string, required
        - object
          - `text` union, required
            - string
            - object
              - …
        - object
          - `role` union, required
            - string
            - object
              - …
        - object
          - `label` string, required
        - object
          - `placeholder` string, required
        - object
          - `id` string, required
        - object
          - `name` string, required
        - object
          - `testId` string, required
          - `testIdAttribute` string
        - object
          - `coordinates` Coordinates, required
            - `x` number, required
            - `y` number, required
            - `space` 'viewport' | 'document' | 'screenshot'
      - `selector` string: Legacy CSS target. Do not combine with target.
      - `frame` union: A same-origin iframe target. Cross-origin frames are reported as metadata only.
        - object
          - `name` string, required
          - `urlIncludes` string
        - object
          - `name` string
          - `urlIncludes` string, required
      - `tabId` string: openTab: the id for the new tab, refused with 400 VALIDATION_FAILED if a tab with that id is open; omitted, a free tab-N id is generated. switchTab and closeTab: the tab to act on. closeTab refuses to close the last open tab, whatever its id (400 TAB_CLOSE_FORBIDDEN). New tabs inherit the resolved viewport, device scale and mobile/touch settings.
      - `url` string
      - `text` string
      - `exact` boolean
      - `key` string: Puppeteer key name, for example Enter, Tab, or Control+A.
      - `amount` number
      - `time` integer
      - `value` union
        - string
        - string[]
      - `expression` string: Trusted-JavaScript only; waitForFunction requires the same plan, account entitlement, and platform-wide emergency switch as injectJS.
      - `files` UploadFile[]
        - union
          - object
            - `base64` string, byte, required
            - `name` string, required
            - `mimeType` string
          - object
            - `s3Key` string, required: Withdrawn on 2026-07-30 and always rejected with 400. Browser uploads execute in the isolated renderer, which has no capture-bucket permission and no account identity to check ownership against. Send the file inline as base64 instead.
            - `name` string, required
      - `expected` string: Assertion expression: exists, visible, checked, unchecked, disabled, enabled, text:VALUE, or equals:VALUE.
      - `continueOnError` boolean
    - `typeSpeed` integer
    - `fastType` boolean
    - `captureTimeline` boolean
    - `extractMarkdown` boolean: Extract agent-readable Markdown. Empty main content falls back to the document body and then the accessibility tree when available.
    - `semanticNodes` boolean: Legacy interactive-node summary. Prefer semanticSnapshot for rich DOM data.
    - `simplifyDom` boolean
    - `lazyScroll` boolean
    - `pdfFormat` string: Paper size for a PDF capture. Matched case-insensitively against LETTER, LEGAL, TABLOID, LEDGER, A0, A1, A2, A3, A4, A5 and A6; anything else is refused with 400 and code PDF_FORMAT_UNSUPPORTED, which names the accepted values. An empty or omitted value takes the A4 default.
    - `landscape` boolean
    - `printBackground` boolean
    - `save` boolean: Persist capture artifacts using the configured finite, plan-bounded retention policy. JSON responses include metadata.captureId, metadata.retentionDays, and metadata.captureExpiresAt. A synchronous binary response exposes X-Domscout-Capture-Id and X-Domscout-Capture-Expires-At. Retrieve owned retained artifacts with GET /capture/{captureId}; that route has no separate S3-key field, but its signed downloadUrl can contain storage-path information and is a secret capability URL. Historic rows with expiresAt null are legacy permanent retention.
    - `captureName` string
    - `retentionDays` union: Plan-gated artifact retention selection. 365 is Enterprise-only. permanent is deprecated: it is still accepted but now resolves to the plan maximum rather than unlimited retention.
      - 7 | 30 | 90 | 365
      - 'permanent'
    - `callbackUrl` string, uri: Legacy per-job callback endpoint. Must be public HTTPS without URL credentials and must pass DNS/IP safety checks before acceptance and at every redirect hop.
    - `callbackSecret` string: Legacy per-job signing secret. It is never returned.
    - `semanticSnapshot` union
      - boolean: true enables the default rich snapshot.
      - object
        - `enabled` boolean
        - `maxNodes` integer
        - `detail` 'summary' | 'full'
        - `selectorBundles` boolean
        - `timing` 'beforeActions' | 'afterActions' | 'both'
        - `includeIframes` boolean
    - `diagnostics` union
      - boolean: true enables all bounded, redacted diagnostics.
      - object
        - `console` boolean
        - `pageErrors` boolean
        - `network` boolean
        - `resources` boolean
        - `timing` boolean
        - `maxEntries` integer
    - `render` RenderOptions
      - `disableAnimations` boolean
      - `colorScheme` 'light' | 'dark' | 'no-preference'
      - `locale` string: BCP 47 locale such as en-US.
      - `timezone` string: IANA timezone such as Asia/Kolkata.
      - `geolocation` Geolocation
        - `latitude` number, required
        - `longitude` number, required
        - `accuracy` number
      - `userAgent` string: Overrides the browser User-Agent string. The API enforces 1024 characters; a longer value is rejected with 400.
      - `transparentBackground` boolean
      - `blockResourceTypes` string[]
      - `blockDomains` string[]
      - `capturePadding` integer
      - `devicePreset` 'desktop' | 'iphone13' | 'pixel7' | 'ipad': Named viewport and device-emulation preset.
      - `reducedMotion` boolean
      - `media` 'screen' | 'print'
    - `cleanup` union
      - boolean: true enables all built-in heuristic cleanup categories.
      - object
        - `enabled` boolean
        - `preserveSelectors` string[]: Elements matching these selectors are never hidden by heuristic cleanup.
    - `extract` ExtractOptions
      - `fields` object, required
      - `strict` boolean: Fail the request when a required field is missing.
    - `renderJs` boolean: Set false to answer from one guarded HTTP GET instead of a browser. Roughly an order of magnitude faster and priced identically, but a page that renders its content with JavaScript returns whatever its server-side HTML says, which is frequently an empty shell. The response always reports metadata.renderJs and metadata.contentQuality so a caller can detect that case and retry with the browser. Refused with 400 VALIDATION_FAILED, naming each field and refunding the reservation, when combined with html, screenshot:true, a field that needs a live page (actions, injectJS, injectCSS, waitForSelector, selector, captureSelector, hideSelectors, forceState, cookies, headers, a non-zero delay, semanticNodes), save:true, or any option that carries a credit surcharge.
    - `fast` boolean: Alias for renderJs:false. Identical behaviour; both spellings are accepted.
    - `screenshot` boolean: POST /scrape only. Put the base64 image back in the response, which /scrape drops by default. Ignored by POST /screenshot, which always returns one. Requires a browser, so it cannot be combined with renderJs:false.
  - object
    - `url` string, uri, required: Public HTTP(S) page URL. Unsafe addresses and redirects are rejected. Credentials in the authority (https://user:pass@host/) are refused with 400 — send authentication in the headers field instead, so it is never written to a capture log.
    - `html` string: Raw HTML to render instead of navigating to a URL.
    - `format` 'png' | 'jpeg' | 'webp' | 'pdf'
    - `responseType` 'json' | 'binary': How the capture comes back. "json" (default) returns a JSON envelope with base64 image data plus metadata and any analysis you requested. "binary" returns the raw file bytes with a matching Content-Type, and therefore has nowhere to carry metadata — so a binary request combined with an analysis option (cleanup, semanticSnapshot, diagnostics, or extract) is AUTO-PROMOTED to a durable job: it answers 202 with a jobId and metadata.autoPromotedAsync, and you poll GET /job/{jobId} for the result. A capture that fails after promotion reports its failure on the job rather than as a synchronous 4xx, carrying the same error code it would have returned inline. Binary responses also cannot carry metadata.ignoredFields.
    - `async` boolean: Force durable asynchronous execution. The service also auto-promotes captures expected to be large (rich JSON output) or slow (a URL capture stacking two or more whole-DOM passes, or one combined with fullPage/lazyScroll). Slow-capture promotion applies to Pro and above; Hobby keeps inline results.
    - `probe` boolean: Load and measure the page without a screenshot.
    - `width` integer
    - `height` integer
    - `isMobile` boolean
    - `hasTouch` boolean
    - `deviceScaleFactor` number
    - `quality` integer
    - `fullPage` boolean: Capture the whole scrollable page rather than the viewport. Adds a credit surcharge. The rendered page may be at most 12000px tall and 3840px wide; beyond that the request is refused with 400 and code CAPTURE_TOO_TALL or CAPTURE_TOO_WIDE rather than a truncated image. The same ceiling applies to captureSelector.
    - `selector` string: Legacy alias for captureSelector.
    - `captureSelector` string: The matched element must be visible within 5000ms, or the request is refused with 400 and code CAPTURE_SELECTOR_NOT_FOUND. It is also subject to the same 12000px ceiling as fullPage (400, CAPTURE_TOO_TALL).
    - `hideSelectors` string[]
    - `captureMode` 'default' | 'grid' | 'skeleton': default uses the requested viewport. grid produces responsive desktop, tablet, and mobile frames without reloading raw inline HTML. skeleton preserves a DOMContentLoaded frame before waiting for bounded hydration, so an inline HTML skeleton is still useful even if later network-idle settling times out. grid and skeleton also honour extractMarkdown; the Markdown is taken from the DOM after the mode has finished capturing, so for grid that is the final (desktop) viewport.
    - `waitForSelector` string: A wait that expires is a client-side outcome, not a server fault: the request is refused with 400 and code WAIT_FOR_SELECTOR_TIMEOUT.
    - `delay` integer
    - `headers` object: Scoped only to requests to the original target origin, including opened tabs and redirects. Sensitive header values do not appear in diagnostics or persisted artifacts.
    - `cookies` Cookie[]
      - `selector` string
      - `name` string, required
      - `value` string, required
      - `domain` string
      - `path` string
      - `httpOnly` boolean
      - `secure` boolean
      - `sameSite` 'Strict' | 'Lax' | 'None'
    - `injectCSS` string
    - `injectJS` string: Trusted-JavaScript feature. It requires an eligible plan, an operator-controlled account entitlement, and the platform-wide emergency switch.
    - `aiMode` boolean
    - `debugOverlay` boolean: Render console errors, page exceptions, failed requests and oversized assets into an overlay on the captured image. At most 200 entries are collected and each is truncated to 2048 characters; past that the overlay ends with a [TRUNCATED] marker. For structured, machine-readable output use `diagnostics` instead.
    - `performanceOverlay` boolean
    - `forceState` ForceState[]
      - `selector` string, required: A CSS selector. If it matches several elements the state is applied to the first, and the result says so.
      - `state` 'hover' | 'focus', required
    - `actions` Action[]
      - `action` 'click' | 'type' | 'scroll' | 'wait' | 'navigate' | 'back' | 'forward' | 'reload' | 'select' | 'check' | 'uncheck' | 'hover' | 'focus' | 'pressKey' | 'scrollIntoView' | 'waitForText' | 'waitForUrl' | 'waitForFunction' | 'waitForRequest' | 'waitForResponse' | 'waitForNetworkIdle' | 'openTab' | 'switchTab' | 'closeTab' | 'assert' | 'upload', required
      - `target` union: A selector-based action target. Semantic snapshot node IDs are inspection evidence only and are intentionally not accepted as durable action targets.
        - object
          - `css` string, required
        - object
          - `xpath` string, required
        - object
          - `text` union, required
            - string
            - object
              - …
        - object
          - `role` union, required
            - string
            - object
              - …
        - object
          - `label` string, required
        - object
          - `placeholder` string, required
        - object
          - `id` string, required
        - object
          - `name` string, required
        - object
          - `testId` string, required
          - `testIdAttribute` string
        - object
          - `coordinates` Coordinates, required
            - `x` number, required
            - `y` number, required
            - `space` 'viewport' | 'document' | 'screenshot'
      - `selector` string: Legacy CSS target. Do not combine with target.
      - `frame` union: A same-origin iframe target. Cross-origin frames are reported as metadata only.
        - object
          - `name` string, required
          - `urlIncludes` string
        - object
          - `name` string
          - `urlIncludes` string, required
      - `tabId` string: openTab: the id for the new tab, refused with 400 VALIDATION_FAILED if a tab with that id is open; omitted, a free tab-N id is generated. switchTab and closeTab: the tab to act on. closeTab refuses to close the last open tab, whatever its id (400 TAB_CLOSE_FORBIDDEN). New tabs inherit the resolved viewport, device scale and mobile/touch settings.
      - `url` string
      - `text` string
      - `exact` boolean
      - `key` string: Puppeteer key name, for example Enter, Tab, or Control+A.
      - `amount` number
      - `time` integer
      - `value` union
        - string
        - string[]
      - `expression` string: Trusted-JavaScript only; waitForFunction requires the same plan, account entitlement, and platform-wide emergency switch as injectJS.
      - `files` UploadFile[]
        - union
          - object
            - `base64` string, byte, required
            - `name` string, required
            - `mimeType` string
          - object
            - `s3Key` string, required: Withdrawn on 2026-07-30 and always rejected with 400. Browser uploads execute in the isolated renderer, which has no capture-bucket permission and no account identity to check ownership against. Send the file inline as base64 instead.
            - `name` string, required
      - `expected` string: Assertion expression: exists, visible, checked, unchecked, disabled, enabled, text:VALUE, or equals:VALUE.
      - `continueOnError` boolean
    - `typeSpeed` integer
    - `fastType` boolean
    - `captureTimeline` boolean
    - `extractMarkdown` boolean: Extract agent-readable Markdown. Empty main content falls back to the document body and then the accessibility tree when available.
    - `semanticNodes` boolean: Legacy interactive-node summary. Prefer semanticSnapshot for rich DOM data.
    - `simplifyDom` boolean
    - `lazyScroll` boolean
    - `pdfFormat` string: Paper size for a PDF capture. Matched case-insensitively against LETTER, LEGAL, TABLOID, LEDGER, A0, A1, A2, A3, A4, A5 and A6; anything else is refused with 400 and code PDF_FORMAT_UNSUPPORTED, which names the accepted values. An empty or omitted value takes the A4 default.
    - `landscape` boolean
    - `printBackground` boolean
    - `save` boolean: Persist capture artifacts using the configured finite, plan-bounded retention policy. JSON responses include metadata.captureId, metadata.retentionDays, and metadata.captureExpiresAt. A synchronous binary response exposes X-Domscout-Capture-Id and X-Domscout-Capture-Expires-At. Retrieve owned retained artifacts with GET /capture/{captureId}; that route has no separate S3-key field, but its signed downloadUrl can contain storage-path information and is a secret capability URL. Historic rows with expiresAt null are legacy permanent retention.
    - `captureName` string
    - `retentionDays` union: Plan-gated artifact retention selection. 365 is Enterprise-only. permanent is deprecated: it is still accepted but now resolves to the plan maximum rather than unlimited retention.
      - 7 | 30 | 90 | 365
      - 'permanent'
    - `callbackUrl` string, uri: Legacy per-job callback endpoint. Must be public HTTPS without URL credentials and must pass DNS/IP safety checks before acceptance and at every redirect hop.
    - `callbackSecret` string: Legacy per-job signing secret. It is never returned.
    - `semanticSnapshot` union
      - boolean: true enables the default rich snapshot.
      - object
        - `enabled` boolean
        - `maxNodes` integer
        - `detail` 'summary' | 'full'
        - `selectorBundles` boolean
        - `timing` 'beforeActions' | 'afterActions' | 'both'
        - `includeIframes` boolean
    - `diagnostics` union
      - boolean: true enables all bounded, redacted diagnostics.
      - object
        - `console` boolean
        - `pageErrors` boolean
        - `network` boolean
        - `resources` boolean
        - `timing` boolean
        - `maxEntries` integer
    - `render` RenderOptions
      - `disableAnimations` boolean
      - `colorScheme` 'light' | 'dark' | 'no-preference'
      - `locale` string: BCP 47 locale such as en-US.
      - `timezone` string: IANA timezone such as Asia/Kolkata.
      - `geolocation` Geolocation
        - `latitude` number, required
        - `longitude` number, required
        - `accuracy` number
      - `userAgent` string: Overrides the browser User-Agent string. The API enforces 1024 characters; a longer value is rejected with 400.
      - `transparentBackground` boolean
      - `blockResourceTypes` string[]
      - `blockDomains` string[]
      - `capturePadding` integer
      - `devicePreset` 'desktop' | 'iphone13' | 'pixel7' | 'ipad': Named viewport and device-emulation preset.
      - `reducedMotion` boolean
      - `media` 'screen' | 'print'
    - `cleanup` union
      - boolean: true enables all built-in heuristic cleanup categories.
      - object
        - `enabled` boolean
        - `preserveSelectors` string[]: Elements matching these selectors are never hidden by heuristic cleanup.
    - `extract` ExtractOptions
      - `fields` object, required
      - `strict` boolean: Fail the request when a required field is missing.
    - `renderJs` boolean: Set false to answer from one guarded HTTP GET instead of a browser. Roughly an order of magnitude faster and priced identically, but a page that renders its content with JavaScript returns whatever its server-side HTML says, which is frequently an empty shell. The response always reports metadata.renderJs and metadata.contentQuality so a caller can detect that case and retry with the browser. Refused with 400 VALIDATION_FAILED, naming each field and refunding the reservation, when combined with html, screenshot:true, a field that needs a live page (actions, injectJS, injectCSS, waitForSelector, selector, captureSelector, hideSelectors, forceState, cookies, headers, a non-zero delay, semanticNodes), save:true, or any option that carries a credit surcharge.
    - `fast` boolean: Alias for renderJs:false. Identical behaviour; both spellings are accepted.
    - `screenshot` boolean: POST /scrape only. Put the base64 image back in the response, which /scrape drops by default. Ignored by POST /screenshot, which always returns one. Requires a browser, so it cannot be combined with renderJs:false.
  - object
    - `url` string, uri: Public HTTP(S) page URL. Unsafe addresses and redirects are rejected. Credentials in the authority (https://user:pass@host/) are refused with 400 — send authentication in the headers field instead, so it is never written to a capture log.
    - `html` string, required: Raw HTML to render instead of navigating to a URL.
    - `format` 'png' | 'jpeg' | 'webp' | 'pdf'
    - `responseType` 'json' | 'binary': How the capture comes back. "json" (default) returns a JSON envelope with base64 image data plus metadata and any analysis you requested. "binary" returns the raw file bytes with a matching Content-Type, and therefore has nowhere to carry metadata — so a binary request combined with an analysis option (cleanup, semanticSnapshot, diagnostics, or extract) is AUTO-PROMOTED to a durable job: it answers 202 with a jobId and metadata.autoPromotedAsync, and you poll GET /job/{jobId} for the result. A capture that fails after promotion reports its failure on the job rather than as a synchronous 4xx, carrying the same error code it would have returned inline. Binary responses also cannot carry metadata.ignoredFields.
    - `async` boolean: Force durable asynchronous execution. The service also auto-promotes captures expected to be large (rich JSON output) or slow (a URL capture stacking two or more whole-DOM passes, or one combined with fullPage/lazyScroll). Slow-capture promotion applies to Pro and above; Hobby keeps inline results.
    - `probe` boolean: Load and measure the page without a screenshot.
    - `width` integer
    - `height` integer
    - `isMobile` boolean
    - `hasTouch` boolean
    - `deviceScaleFactor` number
    - `quality` integer
    - `fullPage` boolean: Capture the whole scrollable page rather than the viewport. Adds a credit surcharge. The rendered page may be at most 12000px tall and 3840px wide; beyond that the request is refused with 400 and code CAPTURE_TOO_TALL or CAPTURE_TOO_WIDE rather than a truncated image. The same ceiling applies to captureSelector.
    - `selector` string: Legacy alias for captureSelector.
    - `captureSelector` string: The matched element must be visible within 5000ms, or the request is refused with 400 and code CAPTURE_SELECTOR_NOT_FOUND. It is also subject to the same 12000px ceiling as fullPage (400, CAPTURE_TOO_TALL).
    - `hideSelectors` string[]
    - `captureMode` 'default' | 'grid' | 'skeleton': default uses the requested viewport. grid produces responsive desktop, tablet, and mobile frames without reloading raw inline HTML. skeleton preserves a DOMContentLoaded frame before waiting for bounded hydration, so an inline HTML skeleton is still useful even if later network-idle settling times out. grid and skeleton also honour extractMarkdown; the Markdown is taken from the DOM after the mode has finished capturing, so for grid that is the final (desktop) viewport.
    - `waitForSelector` string: A wait that expires is a client-side outcome, not a server fault: the request is refused with 400 and code WAIT_FOR_SELECTOR_TIMEOUT.
    - `delay` integer
    - `headers` object: Scoped only to requests to the original target origin, including opened tabs and redirects. Sensitive header values do not appear in diagnostics or persisted artifacts.
    - `cookies` Cookie[]
      - `selector` string
      - `name` string, required
      - `value` string, required
      - `domain` string
      - `path` string
      - `httpOnly` boolean
      - `secure` boolean
      - `sameSite` 'Strict' | 'Lax' | 'None'
    - `injectCSS` string
    - `injectJS` string: Trusted-JavaScript feature. It requires an eligible plan, an operator-controlled account entitlement, and the platform-wide emergency switch.
    - `aiMode` boolean
    - `debugOverlay` boolean: Render console errors, page exceptions, failed requests and oversized assets into an overlay on the captured image. At most 200 entries are collected and each is truncated to 2048 characters; past that the overlay ends with a [TRUNCATED] marker. For structured, machine-readable output use `diagnostics` instead.
    - `performanceOverlay` boolean
    - `forceState` ForceState[]
      - `selector` string, required: A CSS selector. If it matches several elements the state is applied to the first, and the result says so.
      - `state` 'hover' | 'focus', required
    - `actions` Action[]
      - `action` 'click' | 'type' | 'scroll' | 'wait' | 'navigate' | 'back' | 'forward' | 'reload' | 'select' | 'check' | 'uncheck' | 'hover' | 'focus' | 'pressKey' | 'scrollIntoView' | 'waitForText' | 'waitForUrl' | 'waitForFunction' | 'waitForRequest' | 'waitForResponse' | 'waitForNetworkIdle' | 'openTab' | 'switchTab' | 'closeTab' | 'assert' | 'upload', required
      - `target` union: A selector-based action target. Semantic snapshot node IDs are inspection evidence only and are intentionally not accepted as durable action targets.
        - object
          - `css` string, required
        - object
          - `xpath` string, required
        - object
          - `text` union, required
            - string
            - object
              - …
        - object
          - `role` union, required
            - string
            - object
              - …
        - object
          - `label` string, required
        - object
          - `placeholder` string, required
        - object
          - `id` string, required
        - object
          - `name` string, required
        - object
          - `testId` string, required
          - `testIdAttribute` string
        - object
          - `coordinates` Coordinates, required
            - `x` number, required
            - `y` number, required
            - `space` 'viewport' | 'document' | 'screenshot'
      - `selector` string: Legacy CSS target. Do not combine with target.
      - `frame` union: A same-origin iframe target. Cross-origin frames are reported as metadata only.
        - object
          - `name` string, required
          - `urlIncludes` string
        - object
          - `name` string
          - `urlIncludes` string, required
      - `tabId` string: openTab: the id for the new tab, refused with 400 VALIDATION_FAILED if a tab with that id is open; omitted, a free tab-N id is generated. switchTab and closeTab: the tab to act on. closeTab refuses to close the last open tab, whatever its id (400 TAB_CLOSE_FORBIDDEN). New tabs inherit the resolved viewport, device scale and mobile/touch settings.
      - `url` string
      - `text` string
      - `exact` boolean
      - `key` string: Puppeteer key name, for example Enter, Tab, or Control+A.
      - `amount` number
      - `time` integer
      - `value` union
        - string
        - string[]
      - `expression` string: Trusted-JavaScript only; waitForFunction requires the same plan, account entitlement, and platform-wide emergency switch as injectJS.
      - `files` UploadFile[]
        - union
          - object
            - `base64` string, byte, required
            - `name` string, required
            - `mimeType` string
          - object
            - `s3Key` string, required: Withdrawn on 2026-07-30 and always rejected with 400. Browser uploads execute in the isolated renderer, which has no capture-bucket permission and no account identity to check ownership against. Send the file inline as base64 instead.
            - `name` string, required
      - `expected` string: Assertion expression: exists, visible, checked, unchecked, disabled, enabled, text:VALUE, or equals:VALUE.
      - `continueOnError` boolean
    - `typeSpeed` integer
    - `fastType` boolean
    - `captureTimeline` boolean
    - `extractMarkdown` boolean: Extract agent-readable Markdown. Empty main content falls back to the document body and then the accessibility tree when available.
    - `semanticNodes` boolean: Legacy interactive-node summary. Prefer semanticSnapshot for rich DOM data.
    - `simplifyDom` boolean
    - `lazyScroll` boolean
    - `pdfFormat` string: Paper size for a PDF capture. Matched case-insensitively against LETTER, LEGAL, TABLOID, LEDGER, A0, A1, A2, A3, A4, A5 and A6; anything else is refused with 400 and code PDF_FORMAT_UNSUPPORTED, which names the accepted values. An empty or omitted value takes the A4 default.
    - `landscape` boolean
    - `printBackground` boolean
    - `save` boolean: Persist capture artifacts using the configured finite, plan-bounded retention policy. JSON responses include metadata.captureId, metadata.retentionDays, and metadata.captureExpiresAt. A synchronous binary response exposes X-Domscout-Capture-Id and X-Domscout-Capture-Expires-At. Retrieve owned retained artifacts with GET /capture/{captureId}; that route has no separate S3-key field, but its signed downloadUrl can contain storage-path information and is a secret capability URL. Historic rows with expiresAt null are legacy permanent retention.
    - `captureName` string
    - `retentionDays` union: Plan-gated artifact retention selection. 365 is Enterprise-only. permanent is deprecated: it is still accepted but now resolves to the plan maximum rather than unlimited retention.
      - 7 | 30 | 90 | 365
      - 'permanent'
    - `callbackUrl` string, uri: Legacy per-job callback endpoint. Must be public HTTPS without URL credentials and must pass DNS/IP safety checks before acceptance and at every redirect hop.
    - `callbackSecret` string: Legacy per-job signing secret. It is never returned.
    - `semanticSnapshot` union
      - boolean: true enables the default rich snapshot.
      - object
        - `enabled` boolean
        - `maxNodes` integer
        - `detail` 'summary' | 'full'
        - `selectorBundles` boolean
        - `timing` 'beforeActions' | 'afterActions' | 'both'
        - `includeIframes` boolean
    - `diagnostics` union
      - boolean: true enables all bounded, redacted diagnostics.
      - object
        - `console` boolean
        - `pageErrors` boolean
        - `network` boolean
        - `resources` boolean
        - `timing` boolean
        - `maxEntries` integer
    - `render` RenderOptions
      - `disableAnimations` boolean
      - `colorScheme` 'light' | 'dark' | 'no-preference'
      - `locale` string: BCP 47 locale such as en-US.
      - `timezone` string: IANA timezone such as Asia/Kolkata.
      - `geolocation` Geolocation
        - `latitude` number, required
        - `longitude` number, required
        - `accuracy` number
      - `userAgent` string: Overrides the browser User-Agent string. The API enforces 1024 characters; a longer value is rejected with 400.
      - `transparentBackground` boolean
      - `blockResourceTypes` string[]
      - `blockDomains` string[]
      - `capturePadding` integer
      - `devicePreset` 'desktop' | 'iphone13' | 'pixel7' | 'ipad': Named viewport and device-emulation preset.
      - `reducedMotion` boolean
      - `media` 'screen' | 'print'
    - `cleanup` union
      - boolean: true enables all built-in heuristic cleanup categories.
      - object
        - `enabled` boolean
        - `preserveSelectors` string[]: Elements matching these selectors are never hidden by heuristic cleanup.
    - `extract` ExtractOptions
      - `fields` object, required
      - `strict` boolean: Fail the request when a required field is missing.
    - `renderJs` boolean: Set false to answer from one guarded HTTP GET instead of a browser. Roughly an order of magnitude faster and priced identically, but a page that renders its content with JavaScript returns whatever its server-side HTML says, which is frequently an empty shell. The response always reports metadata.renderJs and metadata.contentQuality so a caller can detect that case and retry with the browser. Refused with 400 VALIDATION_FAILED, naming each field and refunding the reservation, when combined with html, screenshot:true, a field that needs a live page (actions, injectJS, injectCSS, waitForSelector, selector, captureSelector, hideSelectors, forceState, cookies, headers, a non-zero delay, semanticNodes), save:true, or any option that carries a credit surcharge.
    - `fast` boolean: Alias for renderJs:false. Identical behaviour; both spellings are accepted.
    - `screenshot` boolean: POST /scrape only. Put the base64 image back in the response, which /scrape drops by default. Ignored by POST /screenshot, which always returns one. Requires a browser, so it cannot be combined with renderJs:false.

## Response `200`

The page as a document.

- ScrapeResponse: A page as a document. The same capture the /screenshot route runs, projected into the shape an agent reads: Markdown and links at the top level, and no base64 image unless screenshot:true asked for one.
  - `status` 'success', required
  - `url` string, required: The canonical URL of the page, falling back to the URL requested.
  - `title` string, required
  - `markdown` string, required: Empty string when the page yielded no extractable text.
  - `links` ExtractedLink[], required: Absolute, de-duplicated, tracking-parameter-stripped links, capped at 100.
    - `text` string, required
    - `href` string, required
  - `metadata` ScrapeMetadata, required: The capture metadata, plus the document fields lifted out of markdownMeta so a caller reads one object rather than two. Present on both render modes with identical field names.
    - `url` string, required: The URL that was requested.
    - `statusCode` integer, nullable: HTTP status of the final response, after redirects. Null when the browser reported none.
    - `httpStatus` integer, nullable: Alias of statusCode, kept for parity with ScreenshotMetadata.
    - `renderJs` boolean, required: Which engine answered: true for the browser, false for the no-browser path. Read this before concluding a page is empty.
    - `description` string
    - `ogTitle` string
    - `ogDescription` string
    - `ogImage` string
    - `wordCount` integer
    - `charCount` integer
    - `estimatedTokens` integer: charCount / 4, rounded up. A budgeting estimate, not a tokenizer result.
    - `contentQuality` ContentQuality (unresolved $ref)
    - `durationMs` integer
    - `truncated` boolean: Present and true when the transfer hit its byte cap.
    - `advice` string: Present when the answer looks thin and a different render mode would likely do better.
    - `ignoredFields` string[]: Top-level request fields this route does not have. A typo is named here rather than rejected.
    - `credits` CaptureCredits: What this request cost and what is left. Present on billable JSON responses only.
      - `cost` integer: Credits this request consumed.
      - `chargedTo` string: Which bucket paid.
      - `remaining` integer, nullable
      - `quotaRemaining` integer, nullable
      - `overage` boolean: True when the paying bucket was beyond the plan's included quota and therefore metered.
  - `screenshotBase64` string: Present only when screenshot:true was sent.
  - `analysis` Analysis: Structured rich analysis is available on every JSON response mode, including grid and skeleton captures.
    - `semanticSnapshot` union
      - SemanticSnapshot
        - `version` integer, required: Versioned snapshot format identifier.
        - `generatedAt` string, date-time
        - `detail` 'summary' | 'full'
        - `document` SnapshotDocument
          - `title` string
          - `url` string
          - `viewport` SnapshotViewport
            - `width` integer
            - `height` integer
            - `devicePixelRatio` number
        - `nodes` SemanticNode[], required
          - `id` string, required: Per-snapshot evidence ID. It is not a cross-request action locator.
          - `stableKey` string: Node identity based on persistent identifiers or structural position, independent of mutable text and accessible names.
          - `tag` string, required
          - `role` string, nullable
          - `accessibleName` string
          - `text` string
          - `attributes` object: Allowed, non-sensitive attributes only.
          - `form` FormState: Form-control state. Field values are intentionally omitted and valueRedacted is true when a control value existed.
            - `checked` boolean, nullable
            - `disabled` boolean
            - `selected` string[], nullable
            - `required` boolean
            - `readOnly` boolean
            - `valid` boolean, nullable
            - `validationMessage` string
            - `valueRedacted` boolean
          - `parentId` string, nullable
          - `childIds` string[]
          - `visible` boolean, required
          - `inViewport` boolean
          - `box` BoundingBox, required
            - `x` number, required
            - `y` number, required
            - `width` number, required
            - `height` number, required
            - `viewportX` number
            - `viewportY` number
            - `screenshotX` number
            - `screenshotY` number
            - `screenshotWidth` number
            - `screenshotHeight` number
            - `normalizedX` number
            - `normalizedY` number
            - `normalizedWidth` number
            - `normalizedHeight` number
          - `screenshotBox` BoundingBox
            - `x` number, required
            - `y` number, required
            - `width` number, required
            - `height` number, required
            - `viewportX` number
            - `viewportY` number
            - `screenshotX` number
            - `screenshotY` number
            - `screenshotWidth` number
            - `screenshotHeight` number
            - `normalizedX` number
            - `normalizedY` number
            - `normalizedWidth` number
            - `normalizedHeight` number
          - `normalizedBox` BoundingBox
            - `x` number, required
            - `y` number, required
            - `width` number, required
            - `height` number, required
            - `viewportX` number
            - `viewportY` number
            - `screenshotX` number
            - `screenshotY` number
            - `screenshotWidth` number
            - `screenshotHeight` number
            - `normalizedX` number
            - `normalizedY` number
            - `normalizedWidth` number
            - `normalizedHeight` number
          - `normalizedScreenshotBox` NormalizedCoordinateBox
            - `x` number, required
            - `y` number, required
            - `width` number, required
            - `height` number, required
          - `viewportIntersection` ViewportIntersection
            - `intersects` boolean
            - `ratio` number
          - `zIndex` integer, nullable
          - `clickable` boolean
          - `obscuredBy` string, nullable: A redacted description of the element returned by elementFromPoint, when safe to report.
          - `selectorBundles` SelectorCandidate[], required
            - `type` 'css' | 'xpath' | 'text' | 'role' | 'id' | 'name' | 'testId' | 'label' | 'placeholder', required
            - `value` unknown, required
            - `score` number, required
            - `matches` integer, required
            - `unique` boolean: Derived from matches === 1 when included.
            - `brittleness` 'low' | 'medium' | 'high'
            - `reasons` string[]
          - `iframe` IframeNodeMetadata, nullable
            - `src` string, nullable
            - `name` string, nullable
            - `accessible` boolean
        - `frames` FrameMetadata[], required
          - `url` string, required
          - `name` string, nullable
          - `sameOrigin` boolean, required
          - `accessible` boolean, required
        - `truncated` boolean
      - object
        - `beforeActions` SemanticSnapshot
          - `version` integer, required: Versioned snapshot format identifier.
          - `generatedAt` string, date-time
          - `detail` 'summary' | 'full'
          - `document` SnapshotDocument
            - `title` string
            - `url` string
            - `viewport` SnapshotViewport
              - …
          - `nodes` SemanticNode[], required
            - `id` string, required: Per-snapshot evidence ID. It is not a cross-request action locator.
            - `stableKey` string: Node identity based on persistent identifiers or structural position, independent of mutable text and accessible names.
            - `tag` string, required
            - `role` string, nullable
            - `accessibleName` string
            - `text` string
            - `attributes` object: Allowed, non-sensitive attributes only.
            - `form` FormState: Form-control state. Field values are intentionally omitted and valueRedacted is true when a control value existed.
              - …
            - `parentId` string, nullable
            - `childIds` string[]
            - `visible` boolean, required
            - `inViewport` boolean
            - `box` BoundingBox, required
              - …
            - `screenshotBox` BoundingBox
              - …
            - `normalizedBox` BoundingBox
              - …
            - `normalizedScreenshotBox` NormalizedCoordinateBox
              - …
            - `viewportIntersection` ViewportIntersection
              - …
            - `zIndex` integer, nullable
            - `clickable` boolean
            - `obscuredBy` string, nullable: A redacted description of the element returned by elementFromPoint, when safe to report.
            - `selectorBundles` SelectorCandidate[], required
              - …
            - `iframe` IframeNodeMetadata, nullable
              - …
          - `frames` FrameMetadata[], required
            - `url` string, required
            - `name` string, nullable
            - `sameOrigin` boolean, required
            - `accessible` boolean, required
          - `truncated` boolean
        - `afterActions` SemanticSnapshot
          - `version` integer, required: Versioned snapshot format identifier.
          - `generatedAt` string, date-time
          - `detail` 'summary' | 'full'
          - `document` SnapshotDocument
            - `title` string
            - `url` string
            - `viewport` SnapshotViewport
              - …
          - `nodes` SemanticNode[], required
            - `id` string, required: Per-snapshot evidence ID. It is not a cross-request action locator.
            - `stableKey` string: Node identity based on persistent identifiers or structural position, independent of mutable text and accessible names.
            - `tag` string, required
            - `role` string, nullable
            - `accessibleName` string
            - `text` string
            - `attributes` object: Allowed, non-sensitive attributes only.
            - `form` FormState: Form-control state. Field values are intentionally omitted and valueRedacted is true when a control value existed.
              - …
            - `parentId` string, nullable
            - `childIds` string[]
            - `visible` boolean, required
            - `inViewport` boolean
            - `box` BoundingBox, required
              - …
            - `screenshotBox` BoundingBox
              - …
            - `normalizedBox` BoundingBox
              - …
            - `normalizedScreenshotBox` NormalizedCoordinateBox
              - …
            - `viewportIntersection` ViewportIntersection
              - …
            - `zIndex` integer, nullable
            - `clickable` boolean
            - `obscuredBy` string, nullable: A redacted description of the element returned by elementFromPoint, when safe to report.
            - `selectorBundles` SelectorCandidate[], required
              - …
            - `iframe` IframeNodeMetadata, nullable
              - …
          - `frames` FrameMetadata[], required
            - `url` string, required
            - `name` string, nullable
            - `sameOrigin` boolean, required
            - `accessible` boolean, required
          - `truncated` boolean
    - `diagnostics` Diagnostics: Bounded, redacted HAR-like diagnostics. It deliberately omits cookies, authorization, form values, and request/response bodies.
      - `console` ConsoleEvent[]
        - `type` string
        - `text` string
        - `at` string, date-time
      - `pageErrors` PageError[]
        - `message` string
        - `at` string, date-time
      - `failedRequests` FailedRequest[]
        - `url` string
        - `method` string
        - `resourceType` string
        - `error` string
      - `responses` NetworkResponse[]
        - `url` string
        - `method` string
        - `status` integer
        - `resourceType` string
        - `fromCache` boolean
      - `redirects` Redirect[]
        - `from` string
        - `to` string
        - `status` integer
      - `resources` ResourceTiming[]
        - `name` string
        - `initiatorType` string
        - `duration` number
        - `transferSize` integer
        - `encodedBodySize` integer
        - `decodedBodySize` integer
      - `summary` DiagnosticsSummary
        - `consoleCount` integer
        - `pageErrorCount` integer
        - `failedRequestCount` integer
        - `responseCount` integer
        - `resourceCount` integer
        - `thirdPartyDomains` string[]
        - `transferBytes` integer
        - `truncated` boolean
      - `truncated` boolean
    - `extraction` ExtractionResult
      - `fields` object, required
    - `cleanup` CleanupReport
      - `removed` integer
      - `categories` object

## Other responses

- `202`: The request was explicitly asynchronous or automatically promoted to a durable job.
- `206`: The action budget ended after some action/timeline work completed. metadata.budgetWarning names the budget that applied: 24s synchronous, 52s for async and other worker-executed captures.
- `400`: The request failed validation.
- `403`: The request was refused. Returned when the x-api-key header is missing, revoked, or invalid, and when a valid key is blocked by a plan entitlement, the trusted-JavaScript account entitlement, a global product pause, or a security policy. A plan-only restriction returns code PLAN_FEATURE_UNAVAILABLE and can include upgrade_url. FEATURE_NOT_ENABLED identifies either the global product emergency pause or an eligible Business account without trusted-JavaScript permission; it carries no upgrade_url, so upgrading or retrying cannot clear it. Browser capture can return BROWSER_ISOLATION_REQUIRED or BROWSER_EXECUTION_DENIED with reason public_capture_not_enabled when browser work is unavailable for your account. These platform restrictions carry no upgrade_url and are not resolved by changing plan or key. Check GET /credits for current account capabilities. The API does not use 401: it authenticates with an API key header rather than an HTTP authentication scheme, so there is no WWW-Authenticate challenge to issue.
- `409`: The Idempotency-Key was already used with a different request body.
- `413`: The requested binary response cannot be served synchronously.
- `415`: renderJs:false was asked to read a non-textual payload (a PDF, an image, a binary download). Retry with renderJs:true, which renders it in the browser.
- `429`: The plan quota or rate limit has been reached.
- `500`: An unexpected service error occurred.
- `503`: A dependency this request needs is unavailable or not configured for this deployment. Codes: SUPABASE_TIMEOUT and SUPABASE_INVALID_RESPONSE (a storage operation did not answer, or answered unreadably — the write may have committed, so any retry must be idempotent), CAPTURE_ARTIFACT_UNAVAILABLE and CAPTURE_ARTIFACT_CORRUPT (a stored artifact could not be read or parsed), ASYNC_JOBS_NOT_CONFIGURED and CAPTURE_RETRIEVAL_NOT_CONFIGURED (the capability is not configured here, on any plan). Retryable unless the code names a deployment configuration.
- `504`: The browser session timed out during navigation or while executing actions, or did not finish before the synchronous capture deadline (`RENDER_DEADLINE_EXCEEDED`). When frames were captured before an action timeout the body includes partialResult and timelineBase64. Retry with async:true to run outside the synchronous window.

---

[API](https://skmtc.dev/domscout/apis/domscout-api.md) · [All operations](https://skmtc.dev/domscout/apis/domscout-api/llms.txt) · [OpenAPI document](https://skmtc.dev/domscout/apis/domscout-api/revisions/20562d5b5d94?raw)
