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

# WebExtrator Web Page Rendering API Integration Guide

> WebExtrator Web Render & Extract integration guide - Ace Data Cloud

`POST https://api.acedata.cloud/webextrator/render`

The WebExtrator Web Page Rendering API is a web rendering service based on headless Chromium. Given a URL, it returns the fully rendered HTML (including content injected by JS), plain text, page title, and final URL.

Render is the lowest-level interface of WebExtrator. If you need **structured** extraction results (article body, product price, recipe ingredients, etc.), please use [`/webextrator/extract`](development_webextrator_extract) — it runs a complete set of typed extraction pipelines on the same rendering basis.

## Application Process

To use the WebExtrator service page, first go to the [Ace Data Cloud Console](https://platform.acedata.cloud/console/applications) to obtain your API Token for backup.

![](https://cdn.acedata.cloud/5hmkdg.jpg)

If you are not logged in or registered, you will be automatically redirected to the login page inviting you to register and log in, and after completion, you will be automatically returned to the current page.

**One API Token can call all services on the platform, no need to apply separately for each service.** The first application will grant a free quota for a free experience; when the quota is insufficient, you can recharge the general balance in the [console](https://platform.acedata.cloud/console/coin).

> 📘 Complete documentation: [WebExtrator Service Page →](https://platform.acedata.cloud/service/webextrator)

## Authentication

All WebExtrator interfaces use standard Bearer Token authentication:

```
Authorization: Bearer YOUR_API_KEY
Content-Type:  application/json
```

## Request Parameters

| Field               | Type      | Required | Default                    | Description                                                                                                                     |
| ------------------- | --------- | :------: | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `url`               | string    |     ✅    | —                          | The URL of the page to render, must be `http(s)://`.                                                                            |
| `user_agent`        | string    |     ❌    | Built-in UA pool rotation  | Custom User-Agent.                                                                                                              |
| `timeout`           | number    |     ❌    | `30`                       | Single navigation timeout (**seconds**).                                                                                        |
| `wait_until`        | enum      |     ❌    | `networkidle`              | Load completion event: `load` / `domcontentloaded` / `networkidle` / `commit`.                                                  |
| `delay`             | number    |     ❌    | `0`                        | **Additional wait seconds** after `wait_until` is triggered (for SPA re-rendering).                                             |
| `wait_for_selector` | string    |     ❌    | —                          | Wait for this CSS selector to appear, more stable than `networkidle`.                                                           |
| `block_resources`   | string\[] |     ❌    | `["image","font","media"]` | Types of resources to block, optional: `image` / `font` / `media` / `stylesheet` / `xhr` / `fetch`.                             |
| `headers`           | object    |     ❌    | —                          | Additional HTTP request headers (e.g., `{"Accept-Language": "en-US"}`).                                                         |
| `cookies`           | array     |     ❌    | —                          | Cookies injected before navigation, structure as below.                                                                         |
| `callback_url`      | string    |     ❌    | —                          | Callback address in asynchronous mode, the platform will `POST` the complete result to this address when the task is completed. |
| `bypass_cache`      | boolean   |     ❌    | `false`                    | Skip Redis cache reading (but will still write this result back to cache).                                                      |
| `cache_ttl_seconds` | number    |     ❌    | `3600`                     | Custom TTL for this write to cache, passing `0` means do not cache this response.                                               |
| `async`             | boolean   |     ❌    | `false`                    | Set to `true` to immediately return `task_id`, results can be retrieved via `callback_url` or Tasks API.                        |

> The platform contract uniformly uses **snake\_case**. The internal rendering service supports camelCase, but external calls always use snake\_case.

### Cookie Structure

```json theme={null}
{
  "name":      "string",
  "value":     "string",
  "domain":    "string",
  "path":      "/",
  "expires":   1735689600,
  "httpOnly":  false,
  "secure":    true,
  "sameSite":  "Lax"
}
```

## Synchronous Response

```json theme={null}
{
  "success": true,
  "task_id": "550e8400-e29b-41d4-a716-446655440000",
  "trace_id": "550e8400-e29b-41d4-a716-446655440001",
  "started_at": 1777717800.123,
  "finished_at": 1777717801.234,
  "elapsed": 1.111,
  "data": {
    "kind": "render",
    "url": "https://example.com",
    "finalUrl": "https://example.com/",
    "title": "Example Domain",
    "status": 200,
    "html": "<!DOCTYPE html><html>...</html>",
    "text": "Example Domain\nThis domain is for use in illustrative examples...",
    "userAgent": "Mozilla/5.0 ...",
    "elapsedMs": 1108
  }
}
```

| Field                | Type           | Description                                                           |
| -------------------- | -------------- | --------------------------------------------------------------------- |
| `data.kind`          | string         | Fixed `"render"`.                                                     |
| `data.url`           | string         | The URL you submitted.                                                |
| `data.finalUrl`      | string         | The final URL after following redirects.                              |
| `data.title`         | string         | The rendered `document.title`.                                        |
| `data.status`        | number \| null | The HTTP status code of the main navigation.                          |
| `data.html`          | string         | The complete rendered HTML.                                           |
| `data.text`          | string         | Snapshot of `document.body.innerText` (use Extract for cleaner body). |
| `data.userAgent`     | string         | The actual UA used.                                                   |
| `data.elapsedMs`     | number         | Time taken for browser rendering only.                                |
| `data.cached`        | boolean?       | `true` when cache is hit.                                             |
| `data.cacheStoredAt` | number?        | Unix millisecond timestamp when the cache entry was first written.    |

## Asynchronous Response

When `async=true` (or providing `callback_url`), it immediately returns (HTTP 200):

```json theme={null}
{
  "success": true,
  "task_id": "550e8400-...",
  "trace_id": "6ba7b810-...",
  "started_at": 1777717800.123
}
```

The result will be pushed via `POST` to `callback_url` (if configured), or actively queried through [`/webextrator/tasks`](development_webextrator_tasks).

### Callback Structure

The platform `POST`s an envelope identical to the synchronous mode to `callback_url`, `Content-Type: application/json`. Returning any `2xx` is considered confirmed; `5xx` will be retried with exponential backoff for about 5 minutes.

## Error Response

| HTTP | `error.code`     | Meaning                                                                                             |
| ---- | ---------------- | --------------------------------------------------------------------------------------------------- |
| 400  | `bad_request`    | The request body did not pass Zod validation (missing `url`, incorrect type, etc.).                 |
| 401  | `unauthorized`   | Missing or invalid `Authorization: Bearer …`.                                                       |
| 402  | (x402)           | Insufficient platform balance, returning x402 payment request envelope.                             |
| 408  | `timeout`        | Navigation exceeded `timeout`.                                                                      |
| 429  | `queue_busy`     | Sync queue is congested, please retry or use `async=true`.                                          |
| 500  | `internal_error` | Unhandled exception on the server side (browser crash, etc.), Worker will automatically retry once. |

Error structure:

```json theme={null}
{
  "success": false,
  "task_id": "...",
  "trace_id": "...",
  "started_at": 1777717800.123,
  "finished_at": 1777717800.135,
  "elapsed": 0.012,
  "error": { "code": "bad_request", "message": "url: Invalid url" }
}
```

## Example

### cURL

```bash theme={null}
curl -X POST https://api.acedata.cloud/webextrator/render \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com",
    "wait_until": "networkidle",
    "block_resources": ["image", "media", "font"]
  }'
```

### Python (requests)

```python theme={null}
import os, requests

API_KEY = os.environ["ACEDATA_API_KEY"]

resp = requests.post(
    "https://api.acedata.cloud/webextrator/render",
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json",
    },
    json={
        "url": "https://example.com",
        "wait_until": "networkidle",
        "block_resources": ["image", "media", "font"],
    },
    timeout=60,
)
resp.raise_for_status()
data = resp.json()["data"]
print(data["title"], data["status"], len(data["html"]))
```

### Node.js (fetch)

```js theme={null}
const apiKey = process.env.ACEDATA_API_KEY;

const res = await fetch('https://api.acedata.cloud/webextrator/render', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    url: 'https://example.com',
    wait_until: 'networkidle',
    block_resources: ['image', 'media', 'font'],
  }),
});
const { data } = await res.json();
console.log(data.title, data.status, data.html.length);
```

### Asynchronous + Callback

```bash theme={null}
curl -X POST https://api.acedata.cloud/webextrator/render \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com",
    "async": true,
    "callback_url": "https://your-app.example.com/hooks/webextrator"
  }'
```

Immediately returns `{ "success": true, "task_id": "...", "trace_id": "...", "started_at": 1777717800.123 }`;
When the task is completed, the platform will POST the complete result to your `callback_url`.

### Force Bypass Cache

```bash theme={null}
curl -X POST https://api.acedata.cloud/webextrator/render \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com",
    "bypass_cache": true
  }'
```

## Tips and Pitfalls

* **Choosing `wait_until` correctly is important.** `networkidle` is the most stable but slowest; `domcontentloaded` is fast but may miss asynchronously injected content; `load` is suitable for traditional static pages.
* **Cache Key ignores `async`.** Synchronous and asynchronous requests for the same URL hit the same cache entry, switching freely will not invalidate it.
* **Cache Key ignores `bypass_cache` and `cache_ttl_seconds`.** These two are operational switches and do not affect the response content.
* **`cookies` and `headers` will bucket cache.** Customizing these two will cause the first hit of the same combination to fail.
* **Heavy SPAs often exceed the default 30 seconds.** It is recommended to use `timeout: 60`, `wait_until: "domcontentloaded"`, `delay: 4`, and combine with `wait_for_selector` to wait for the elements of real concern.
* **`block_resources` is the fastest way to reduce latency.** By default, images/fonts/media are blocked; if your extraction does not rely on CSS layout, adding `stylesheet` can make it even faster.
