> ## Documentation Index
> Fetch the complete documentation index at: https://docs.adscrawl.net/llms.txt
> Use this file to discover all available pages before exploring further.

# Render HTML, Markdown, or Article JSON

> Fetch a fully rendered page in raw HTML, readable Markdown, or structured article JSON, routed through residential proxies.

Use `POST /html` to load any public URL in a real browser and retrieve the rendered output in the format your application needs. Choose `"html"` to get the full DOM, `"markdown"` to get a clean readable article, or `"json"` to get a structured Readability payload with title, author, excerpt, and body content. The request is routed through residential proxies with randomized browser fingerprints.

<ParamField body="url" type="string" required>
  Target page URL. Only ports 80 and 443 are supported.
</ParamField>

<ParamField body="contentMode" type="string">
  Controls the response format. Accepted values: `"html"` (default), `"markdown"`, `"json"`. Both `"markdown"` and `"json"` extract readable article content using Readability.
</ParamField>

<ParamField body="selector" type="string">
  Wait for the first matching CSS element before extracting. When `contentMode` is `"html"`, only that element's HTML is returned. When `"markdown"` or `"json"`, Readability runs against that element. Returns `422 CONTENT_SELECTOR_NOT_FOUND` if the selector is not found.
</ParamField>

<ParamField body="waitUntil" type="string">
  Navigation event to wait for before extracting content. Defaults to `"load"`.

  | Value | Behaviour |
  | - | - |
  | `"domcontentloaded"` | Waits for DOMContentLoaded. HTML is parsed without waiting for secondary resources such as images. Recommended for HTML extraction. |
  | `"load"` | Waits for `window.load` after the page and dependent resources (images, stylesheets) finish loading. |
  | `"networkidle"` | Waits until there are no network connections for at least 500 ms. Long polling, analytics, or lazy-loaded resources may cause a timeout. |
</ParamField>

<ParamField body="timeoutMs" type="number">
  Maximum time to wait for the task to complete, in milliseconds. Must be a positive integer no greater than `3,600,000`. Values outside this range fall back to the server default.
</ParamField>

<ParamField body="viewport" type="object">
  Viewport dimensions used when rendering the page.

  <Expandable title="viewport fields">
    <ParamField body="width" type="number">Viewport width in pixels.</ParamField>
    <ParamField body="height" type="number">Viewport height in pixels.</ParamField>
  </Expandable>
</ParamField>

<ParamField body="locale" type="string">
  Browser locale, for example `"en-US"` or `"zh-CN"`. Affects `navigator.language` and `Accept-Language` headers.
</ParamField>

<ParamField body="timezoneId" type="string">
  IANA timezone identifier, for example `"Asia/Shanghai"` or `"America/New_York"`.
</ParamField>

<ParamField body="geolocation" type="object">
  Geolocation coordinates exposed to the page via the Geolocation API.

  <Expandable title="geolocation fields">
    <ParamField body="latitude" type="number">Latitude in decimal degrees.</ParamField>
    <ParamField body="longitude" type="number">Longitude in decimal degrees.</ParamField>
  </Expandable>
</ParamField>

<ParamField body="proxy" type="object">
  Custom proxy configuration. Cannot be combined with `countryCode`. Provide either `server` or the split form (`protocol` + `host` + `port`). Credentials must not be embedded in `server`.

  <Expandable title="proxy fields">
    <ParamField body="server" type="string">Full proxy URL such as `http://host:port` or `socks5://host:port`. Cannot be combined with `host`.</ParamField>
    <ParamField body="protocol" type="string">`"http"` or `"socks5"`. Used in the split form.</ParamField>
    <ParamField body="host" type="string">Proxy host. Used in the split form.</ParamField>
    <ParamField body="port" type="number | string">Port from 1 to 65535.</ParamField>
    <ParamField body="username" type="string">Proxy username. Must be supplied together with `password`.</ParamField>
    <ParamField body="password" type="string">Proxy password. Must be supplied together with `username`.</ParamField>
  </Expandable>
</ParamField>

<ParamField body="countryCode" type="string">
  Managed residential proxy region. Cannot be combined with `proxy`.

  * `"GLOBAL"`: dynamic exit from 15 popular regions.
  * Two-letter country code (e.g. `"US"`, `"DE"`): prefers a trusted proxy for that region with dynamic fallback.
  * Omitted: a random trusted proxy is selected automatically.
</ParamField>

<ParamField body="userAgentMode" type="string">
  `"random"` lets the server pick a User-Agent from its library. Requests without an explicit `userAgent` already default to `"random"`.
</ParamField>

<ParamField body="userAgentOs" type="string">
  Operating system used when `userAgentMode` is `"random"`. Accepted values: `"windows"` (default) or `"macos"`.
</ParamField>

<ParamField body="userAgent" type="string">
  Explicit User-Agent string. Overrides the random selection.
</ParamField>

<ParamField body="fingerprint" type="object">
  Browser fingerprint settings. When omitted, every signal defaults to random while keeping OS, GPU, CPU, memory, fonts, and device signals coherent.

  <Expandable title="fingerprint fields">
    <ParamField body="webRtc" type="string">`"forward"` uses the proxy exit address. `"real"` or `"disabled"` also accepted.</ParamField>
    <ParamField body="webGl" type="string">`"random"` or `"real"`. Controls WebGL vendor and renderer metadata.</ParamField>
    <ParamField body="webGpu" type="string">`"random"`, `"real"`, or `"disabled"`. Random mode follows the WebGL GPU setting.</ParamField>
    <ParamField body="webGlImage" type="string">`"random"` or `"real"`. Controls WebGL image noise.</ParamField>
    <ParamField body="canvas" type="string">`"random"` or `"real"`. Controls canvas noise.</ParamField>
    <ParamField body="audioContext" type="string">`"random"` or `"real"`. Controls audio fingerprint noise.</ParamField>
    <ParamField body="clientRects" type="string">`"random"` or `"real"`. Controls layout measurement noise.</ParamField>
    <ParamField body="speechVoices" type="string">`"random"` or `"real"`. Returns an OS-matched speech voice list.</ParamField>
    <ParamField body="fonts" type="string">`"random"` or `"real"`. Returns an OS-matched font list.</ParamField>
    <ParamField body="hardware" type="string">`"random"` or `"real"`. Generates a coherent CPU thread count and memory pair.</ParamField>
    <ParamField body="doNotTrack" type="string">`"random"`, `"enabled"`, or `"disabled"`.</ParamField>
  </Expandable>
</ParamField>

<ParamField body="cookies" type="array">
  Cookie list injected into the browser context before navigation. Each cookie object requires `name`, `value`, and `domain`.

  <Expandable title="cookie fields">
    <ParamField body="name" type="string" required>Cookie name.</ParamField>
    <ParamField body="value" type="string" required>Cookie value.</ParamField>
    <ParamField body="domain" type="string" required>Target domain such as `.example.com`.</ParamField>
    <ParamField body="path" type="string">Cookie path. Defaults to `/`.</ParamField>
    <ParamField body="secure" type="boolean">Whether the cookie is sent only over HTTPS.</ParamField>
    <ParamField body="httpOnly" type="boolean">Whether the cookie is inaccessible to client-side JavaScript.</ParamField>
    <ParamField body="sameSite" type="string">SameSite attribute.</ParamField>
    <ParamField body="session" type="boolean">Set `true` for a session cookie.</ParamField>
    <ParamField body="expirationDate" type="number">Unix expiry timestamp in seconds. Also accepted as `expires` or `expiry`.</ParamField>
  </Expandable>
</ParamField>

## Response

<ResponseField name="title" type="string">
  Article title extracted from the page.
</ResponseField>

<ResponseField name="byline" type="string">
  Author name or byline, if present.
</ResponseField>

<ResponseField name="excerpt" type="string">
  A short summary or description of the article.
</ResponseField>

<ResponseField name="siteName" type="string">
  Name of the site, if available.
</ResponseField>

<ResponseField name="lang" type="string">
  Language code of the article, e.g. `"en"`.
</ResponseField>

<ResponseField name="dir" type="string">
  Text direction, e.g. `"ltr"` or `"rtl"`. `null` if not detected.
</ResponseField>

<ResponseField name="content" type="string">
  Cleaned HTML of the article body.
</ResponseField>

<ResponseField name="textContent" type="string">
  Plain-text version of the article body with whitespace normalised.
</ResponseField>

<ResponseField name="length" type="number">
  Character count of `textContent`.
</ResponseField>

<ResponseField name="publishedTime" type="string">
  ISO 8601 publication timestamp, or `null` if not found.
</ResponseField>

| Code | Meaning |
| - | - |
| `200` | Success. Returns text/html, text/markdown, or application/json depending on contentMode. |
| `400` | Invalid JSON, URL, contentMode, cookies, proxy, region, or User-Agent. Body over 1 MiB. |
| `401` | Missing or invalid `x-api-key`. |
| `402` | Insufficient balance. Returns `INSUFFICIENT_CREDITS`, `balance`, and `requiredCredits`. |
| `422` | Selector not found (`CONTENT_SELECTOR_NOT_FOUND`) or readability missing (`READABILITY_CONTENT_NOT_FOUND`). |
| `429` | Task rate limited. |
| `502` | Proxy unreachable or target HTTP failure. |
| `503` | Queue, Worker, managed proxy, or User-Agent resources unavailable. |
| `504` | Task, navigation, or proxy connection timed out. |
| `500` | Unclassified task execution failure. |

<Note>
  One credit is consumed after request validation but before the task is enqueued. Request bodies are limited to 1 MiB.
</Note>

## More examples

<CodeGroup>
  ```bash cURL theme={null}
  curl -sS -X POST "https://api.adscrawl.net/html" \
    -H "content-type: application/json" \
    -H "x-api-key: YOUR_API_KEY" \
    -d '{
      "url": "https://example.com",
      "contentMode": "html",
      "selector": "main",
      "waitUntil": "domcontentloaded",
      "countryCode": "US"
    }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch("https://api.adscrawl.net/html", {
    method: "POST",
    headers: {
      "content-type": "application/json",
      "x-api-key": "YOUR_API_KEY",
    },
    body: JSON.stringify({
      url: "https://example.com",
      contentMode: "html",
      selector: "main",
      waitUntil: "domcontentloaded",
      countryCode: "US",
    }),
  });

  const html = await response.text();
  console.log(html);
  ```

  ```python Python theme={null}
  import httpx

  response = httpx.post(
      "https://api.adscrawl.net/html",
      headers={
          "content-type": "application/json",
          "x-api-key": "YOUR_API_KEY",
      },
      json={
          "url": "https://example.com",
          "contentMode": "html",
          "selector": "main",
          "waitUntil": "domcontentloaded",
          "countryCode": "US",
      },
  )

  response.raise_for_status()
  print(response.text)
  ```
</CodeGroup>

<RequestExample>
  ```bash cURL theme={null}
  curl -sS -X POST "https://api.adscrawl.net/html" \
    -H "content-type: application/json" \
    -H "x-api-key: YOUR_API_KEY" \
    -d '{
      "url": "https://example.com/article",
      "contentMode": "json",
      "waitUntil": "domcontentloaded",
      "viewport": { "width": 1280, "height": 720 },
      "locale": "en-US",
      "countryCode": "GLOBAL",
      "userAgentMode": "random",
      "userAgentOs": "windows"
    }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch("https://api.adscrawl.net/html", {
    method: "POST",
    headers: {
      "content-type": "application/json",
      "x-api-key": "YOUR_API_KEY",
    },
    body: JSON.stringify({
      url: "https://example.com/article",
      contentMode: "json",
      waitUntil: "domcontentloaded",
      viewport: { width: 1280, height: 720 },
      locale: "en-US",
      countryCode: "GLOBAL",
      userAgentMode: "random",
      userAgentOs: "windows",
    }),
  });

  if (!response.ok) {
    const err = await response.json();
    throw new Error(`${response.status} ${err.code}: ${err.error}`);
  }

  const article = await response.json();
  console.log(article.title, article.byline);
  ```

  ```python Python theme={null}
  import httpx, json

  response = httpx.post(
      "https://api.adscrawl.net/html",
      headers={
          "content-type": "application/json",
          "x-api-key": "YOUR_API_KEY",
      },
      json={
          "url": "https://example.com/article",
          "contentMode": "json",
          "waitUntil": "domcontentloaded",
          "viewport": {"width": 1280, "height": 720},
          "locale": "en-US",
          "countryCode": "GLOBAL",
          "userAgentMode": "random",
          "userAgentOs": "windows",
      },
  )

  response.raise_for_status()
  article = response.json()
  print(article["title"], article["byline"])
  ```
</RequestExample>

<ResponseExample>
  ```json 200 JSON (contentMode: json) theme={null}
  {
    "title": "Example Article",
    "byline": "OpenAI",
    "excerpt": "A concise article summary.",
    "siteName": "Example",
    "lang": "en",
    "dir": null,
    "content": "<div><p>Readable body...</p></div>",
    "textContent": "Readable body...",
    "length": 2487,
    "publishedTime": null
  }
  ```

  ```markdown 200 Markdown (contentMode: markdown) theme={null}
  # Example Article

  Readable body...

  - key point one
  - key point two
  ```

  ```json 402 Insufficient credits theme={null}
  {
    "error": "Insufficient credits",
    "code": "INSUFFICIENT_CREDITS",
    "balance": 0,
    "requiredCredits": 1
  }
  ```

  ```json 422 Readability not found theme={null}
  {
    "error": "Readable article content was not found",
    "code": "READABILITY_CONTENT_NOT_FOUND"
  }
  ```

  ```json 400 Bad country code theme={null}
  {
    "error": "countryCode is not supported",
    "code": "INVALID_COUNTRY_CODE"
  }
  ```

  ```json 503 Proxy unavailable theme={null}
  {
    "error": "Dynamic country/region routing is unavailable",
    "code": "DYNAMIC_PROXY_NOT_CONFIGURED"
  }
  ```
</ResponseExample>
