> ## 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 網頁渲染 API 集成指南

> WebExtrator Web Render & Extract 整合指南 - Ace Data Cloud

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

WebExtrator 網頁渲染 API 是一個基於無頭 Chromium 的網頁渲染服務。給一個 URL，
返回完全渲染後的 HTML（包括 JS 注入的內容）、純文本、頁面標題和最終 URL。

Render 是 WebExtrator 最底層的接口。如果你需要**結構化的**抽取結果（文章正文、
商品價格、食譜配料 …），請使用
[`/webextrator/extract`](development_webextrator_extract) —— 它在同樣的渲染基礎
上跑了一整套類型化抽取流水線。

## 申請流程

要使用 WebExtrator 服務頁，首先到 [Ace Data Cloud 控制台](https://platform.acedata.cloud/console/applications) 獲取您的 API Token，留作備用。

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

如果你尚未登入或註冊，會自動跳轉到登入頁面邀請你註冊和登入，完成後會自動返回當前頁面。

**一個 API Token 即可調用平台所有服務，無需為每個服務單獨申請。** 首次申請會贈送免費額度，可免費體驗；額度不足時可在 [控制台](https://platform.acedata.cloud/console/coin) 充值通用餘額。

> 📘 完整文檔：[WebExtrator 服務頁 →](https://platform.acedata.cloud/service/webextrator)

## 鑑權

所有 WebExtrator 接口使用標準的 Bearer Token 鑑權：

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

## 請求參數

| 字段                  | 類型        |  必填 | 默認                         | 說明                                                                      |
| ------------------- | --------- | :-: | -------------------------- | ----------------------------------------------------------------------- |
| `url`               | string    |  ✅  | —                          | 要渲染的頁面 URL，必須是 `http(s)://`。                                            |
| `user_agent`        | string    |  ❌  | 內置 UA 池輪換                  | 自定義 User-Agent。                                                         |
| `timeout`           | number    |  ❌  | `30`                       | 單次導航超時（**秒**）。                                                          |
| `wait_until`        | enum      |  ❌  | `networkidle`              | 加載完成事件：`load` / `domcontentloaded` / `networkidle` / `commit`。          |
| `delay`             | number    |  ❌  | `0`                        | `wait_until` 觸發後的**額外等待秒數**（用於 SPA 二次渲染）。                               |
| `wait_for_selector` | string    |  ❌  | —                          | 等待該 CSS 選擇器出現，比 `networkidle` 更穩。                                       |
| `block_resources`   | string\[] |  ❌  | `["image","font","media"]` | 屏蔽的資源類型，可選：`image` / `font` / `media` / `stylesheet` / `xhr` / `fetch`。 |
| `headers`           | object    |  ❌  | —                          | 額外的 HTTP 請求頭（例如 `{"Accept-Language": "en-US"}`）。                        |
| `cookies`           | array     |  ❌  | —                          | 導航前注入的 Cookie，結構見下。                                                     |
| `callback_url`      | string    |  ❌  | —                          | 異步模式下的回調地址，平台在任務完成時 `POST` 完整結果到此地址。                                    |
| `bypass_cache`      | boolean   |  ❌  | `false`                    | 跳過 Redis 緩存讀取（但仍會把本次結果寫回緩存）。                                            |
| `cache_ttl_seconds` | number    |  ❌  | `3600`                     | 自定義本次寫入的緩存 TTL，傳 `0` 表示不緩存本次響應。                                         |
| `async`             | boolean   |  ❌  | `false`                    | 設為 `true` 立即返回 `task_id`，結果通過 `callback_url` 或 Tasks API 取回。            |

> 平台契約統一使用 **snake\_case**。內部渲染服務支持 camelCase，但對外調用一律
> 使用 snake\_case。

### Cookie 結構

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

## 同步響應

```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
  }
}
```

| 字段                   | 類型             | 說明                                                |
| -------------------- | -------------- | ------------------------------------------------- |
| `data.kind`          | string         | 固定 `"render"`。                                    |
| `data.url`           | string         | 你提交的 URL。                                         |
| `data.finalUrl`      | string         | 跟隨重定向後的最終 URL。                                    |
| `data.title`         | string         | 渲染後的 `document.title`。                            |
| `data.status`        | number \| null | 主導航的 HTTP 狀態碼。                                    |
| `data.html`          | string         | 完整的渲染後 HTML。                                      |
| `data.text`          | string         | `document.body.innerText` 快照（如需更乾淨的正文請用 Extract）。 |
| `data.userAgent`     | string         | 實際使用的 UA。                                         |
| `data.elapsedMs`     | number         | 僅瀏覽器渲染耗時。                                         |
| `data.cached`        | boolean?       | 命中緩存時為 `true`。                                    |
| `data.cacheStoredAt` | number?        | 緩存條目首次寫入的 Unix 毫秒時間戳。                             |

## 異步響應

`async=true`（或提供 `callback_url`）時立即返回（HTTP 200）：

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

結果將通過 `POST` 推送到 `callback_url`（如果配置了），或者通過
[`/webextrator/tasks`](development_webextrator_tasks) 主動查詢。

### 回調結構

平台 `POST` 與同步模式**完全相同**的 envelope 到 `callback_url`，
`Content-Type: application/json`。返回任意 `2xx` 即視為已確認；`5xx` 會被
指數退避重試約 5 分鐘。

## 錯誤響應

| HTTP | `error.code`     | 含義                                |
| ---- | ---------------- | --------------------------------- |
| 400  | `bad_request`    | 請求體未通過 Zod 驗證（缺 `url`、類型不對 …）。    |
| 401  | `unauthorized`   | 缺失或無效的 `Authorization: Bearer …`。 |
| 402  | (x402)           | 平台餘額不足，返回 x402 付款要求 envelope。     |
| 408  | `timeout`        | 導航超過 `timeout`。                   |
| 429  | `queue_busy`     | 同步隊列擁擠，請重試或改用 `async=true`。       |
| 500  | `internal_error` | 伺服器端未處理異常（瀏覽器崩潰等），Worker 自動重試一次。  |

錯誤結構：

```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" }
}
```

## 示例

### 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);
```

### 非同步 + 回調

```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"
  }'
```

立即返回 `{ "success": true, "task_id": "...", "trace_id": "...", "started_at": 1777717800.123 }`；
任務完成時平台會 POST 完整結果到你的 `callback_url`。

### 強制繞過快取

```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
  }'
```

## 提示與坑

* **`wait_until` 選對很重要。** `networkidle` 最穩但最慢；`domcontentloaded`
  快但可能漏掉非同步注入的內容；`load` 適合傳統靜態頁。
* **快取 Key 忽略 `async`。** 同一 URL 的同步與非同步請求命中同一快取條目，
  隨意切換不會失效。
* **快取 Key 忽略 `bypass_cache` 與 `cache_ttl_seconds`。** 這兩個是操作開關，
  不影響回應內容。
* **`cookies` 與 `headers` 會分桶快取。** 自訂這兩個會讓首次相同組合命中失敗。
* **重 SPA 經常超過預設 30 秒。** 建議 `timeout: 60`、`wait_until: "domcontentloaded"`、`delay: 4`，再配合 `wait_for_selector` 等待真正關心
  的元素。
* **`block_resources` 是降低延遲的最快路徑。** 預設已屏蔽圖片 / 字體 / 媒體；
  如果你抽取不依賴 CSS 佈局，加上 `stylesheet` 還能更快。
