> ## 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 API guide - 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 토큰을 받아두세요.

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

로그인 또는 등록하지 않은 경우 자동으로 로그인 페이지로 리디렉션되어 등록 및 로그인을 초대합니다. 완료 후 현재 페이지로 자동으로 돌아옵니다.

**하나의 API 토큰으로 플랫폼의 모든 서비스를 호출할 수 있으며, 각 서비스마다 별도로 신청할 필요가 없습니다.** 처음 신청 시 무료 할당량이 제공되어 무료로 체험할 수 있습니다; 할당량이 부족할 경우 [콘솔](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     |  ❌  | —                          | 탐색 전에 주입할 쿠키, 구조는 아래와 같습니다.                                                     |
| `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를 사용합니다.

### 쿠키 구조

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

결과는 `callback_url`로 `POST`로 푸시됩니다(구성된 경우), 또는
[`/webextrator/tasks`](development_webextrator_tasks)를 통해 수동으로 조회할 수 있습니다.

### 콜백 구조

플랫폼은 `callback_url`로 동기 모드와 **완전히 동일한** envelope를 `POST`합니다,
`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 }`를 반환합니다;
작업이 완료되면 플랫폼이 당신의 `callback_url`에 전체 결과를 POST합니다.

### 강제 캐시 우회

```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`는 전통적인 정적 페이지에 적합합니다.
* **캐시 키는 `async`를 무시합니다.** 동일한 URL의 동기 및 비동기 요청이 동일한 캐시 항목에 적중하며, 임의로 전환해도 무효화되지 않습니다.
* **캐시 키는 `bypass_cache` 및 `cache_ttl_seconds`를 무시합니다.** 이 두 가지는 작동 스위치이며, 응답 내용에 영향을 미치지 않습니다.
* **`cookies` 및 `headers`는 분리된 캐시를 생성합니다.** 이 두 가지를 사용자 정의하면 최초 동일 조합의 적중이 실패할 수 있습니다.
* **SPA는 기본 30초를 자주 초과합니다.** `timeout: 60`, `wait_until: "domcontentloaded"`, `delay: 4`를 권장하며, `wait_for_selector`와 함께 진정으로 관심 있는 요소를 기다립니다.
* **`block_resources`는 지연을 줄이는 가장 빠른 경로입니다.** 기본적으로 이미지 / 글꼴 / 미디어가 차단되어 있으며; CSS 레이아웃에 의존하지 않는 경우 `stylesheet`를 추가하면 더 빨라질 수 있습니다.
