> ## 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/extract`

WebExtrator 스마트 추출 API는 URL을 **유형화된 구조화 결과**로 변환합니다 —— 기사, 상품, 레시피, 비디오, 토론, 채용 등, 동시에 정리된 Markdown 및 순수 텍스트를 제공합니다. 원시 HTML이 아닌 "깨끗한 구조화 데이터"가 필요할 때 사용하는 인터페이스입니다.

기본적으로는 세 단계의 파이프라인으로 구성되어 있습니다:

1. **schema.org JSON-LD 매퍼** —— 결정적이며, LLM 비용이 없습니다. Wikipedia / BestBuy / AllRecipes / YouTube / 대부분의 뉴스 / 대부분의 상품 페이지를 커버합니다.
2. **유형화 LLM 추출** —— schema.org가 미치지 못할 때만 트리거됩니다. 페이지 유형에 따라 Schema를 선택하고, Zod로 엄격하게 검증합니다.
3. **Readability + Markdown 보완** —— 항상 실행되며, 앞의 두 단계에서 채워지지 않은 최상위 필드를 보완합니다.

URL 중복 요청은 Redis 결과 캐시에 의해 처리되며, \<1 ms에 반환됩니다.

## 신청 절차

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)

## 인증

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

## 요청 매개변수

Extract는 **모든** [Render API](development_webextrator_render) 매개변수(`url`, `user_agent`, `timeout`, `wait_until`, `delay`, `wait_for_selector`, `block_resources`, `headers`, `cookies`, `callback_url`, `bypass_cache`, `cache_ttl_seconds`, `async`)를 수용하며, 두 개의 Extract 전용 필드가 추가됩니다:

| 필드              | 유형      |  필수 | 기본값     | 설명                                                                                                         |
| --------------- | ------- | :-: | ------- | ---------------------------------------------------------------------------------------------------------- |
| `expected_type` | enum    |  ❌  | 자동 판단   | 페이지 유형 힌트: `product` / `article` / `general`. URL / 텍스트 휴리스틱을 건너뛰고 직접 해당 분기로 진행합니다.                        |
| `enable_llm`    | boolean |  ❌  | `false` | schema.org가 미치지 못할 때 LLM 추출을 허용합니다. Amazon / HN / Greenhouse와 같은 JSON-LD가 없는 페이지에서 열어야 유형화된 결과를 얻을 수 있습니다. |

> 페이지에 schema.org JSON-LD가 포함되어 있을 경우, `enable_llm`은 무효입니다 —— 결정적 매퍼가 직접 결과를 생성하며, LLM 호출을 낭비하지 않습니다. 당신은 **공짜로** 유형화된 결과를 얻습니다.

## 동기 응답

```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": 1777717802.535,
  "elapsed": 2.412,
  "data": {
    "kind": "extract",
    "url": "https://en.wikipedia.org/wiki/Diffbot",
    "finalUrl": "https://en.wikipedia.org/wiki/Diffbot",
    "contentType": "article",
    "title": "Diffbot",
    "description": "미국의 기계 학습 및 지식 관리 회사",
    "byline": "위키미디어 프로젝트 기여자",
    "language": "en",
    "siteName": "Wikipedia",
    "publishedAt": "2007-08-08T05:47:27Z",
    "images": ["https://en.wikipedia.org/static/images/icons/enwiki-25.svg"],
    "links": ["https://en.wikipedia.org/wiki/Machine_learning"],
    "markdown": "# Diffbot\n\nDiffbot은 기계 학습 개발자입니다 ...",
    "text": "Diffbot은 기계 학습 알고리즘의 개발자입니다 ...",
    "structured": {
      "schemaOrg": { "primary": { /* 유형화된 엔티티 */ }, "breadcrumbs": [], "all": [] },
      "openGraph": { "title": "...", "description": "...", "image": "...", "type": "..." },
      "jsonLd": [ /* 원본 JSON-LD */ ]
    },
    "rawSignals": {
      "hasJsonLd": true,
      "title": "Diffbot - Wikipedia",
      "metaDescription": null,
      "pageStatus": 200,
      "textLength": 11473
    },
    "elapsedMs": 2412
  }
}
```

### 최상위 필드

| 필드              | 유형        | 설명                                                                                            |
| --------------- | --------- | --------------------------------------------------------------------------------------------- |
| `kind`          | string    | 고정된 `"extract"`입니다.                                                                           |
| `url`           | string    | 제출한 URL입니다.                                                                                   |
| `finalUrl`      | string    | 리디렉션된 최종 URL입니다.                                                                              |
| `contentType`   | enum      | `product` / `article` / `general`, `expected_type` → schema.org primary → 휴리스틱 순으로 결정됩니다.     |
| `title`         | string    | Readability `<title>` 또는 렌더링된 `document.title`입니다.                                            |
| `description`   | string?   | 우선순위: `<meta name="description" />` → `og:description` → schema.org / LLM 추출 → 본문 첫 단락 절단입니다. |
| `byline`        | string?   | 저자 / 채널 / 회사입니다. 출처 `<meta name="author" />` → schema.org / LLM입니다.                           |
| `language`      | string?   | `<html lang>`입니다.                                                                             |
| `siteName`      | string?   | `og:site_name`입니다.                                                                            |
| `publishedAt`   | string?   | ISO 8601입니다. 우선순위: `article:published_time` → `<time datetime>` → schema.org / LLM입니다.        |
| `images`        | string\[] | 최대 50개의 `<img src />`로, 절대 URL로 변환되고 중복 제거되며 `data:` URI는 버려집니다.                              |
| `links`         | string\[] | 최대 100개의 외부 링크로, 조각 / `javascript:` / `mailto:` / `tel:`가 필터링됩니다.                             |
| `markdown`      | string    | Turndown으로 변환된 Markdown입니다.                                                                   |
| `text`          | string    | Mozilla Readability로 추출된 `textContent`입니다.                                                    |
| `structured`    | object    | 전체 구조화된 결과입니다.                                                                                |
| `rawSignals`    | object    | 디버깅용 진단 정보입니다.                                                                                |
| `cached`        | boolean?  | 캐시에 적중할 경우 `true`입니다.                                                                         |
| `cacheStoredAt` | number?   | 캐시 항목이 처음 기록된 Unix 밀리초 타임스탬프입니다.                                                              |

### `data.structured` 하위 필드

| 자식 필드       | 언제 나타나는지           | 설명                                                                                       |
| ----------- | ------------------ | ---------------------------------------------------------------------------------------- |
| `schemaOrg` | 항상                 | `{ primary, breadcrumbs, all }`。`primary`는 가장 높은 우선순위의 유형화된 엔티티이며, 찾을 수 없을 경우 `null`입니다. |
| `openGraph` | 항상                 | `{ title, description, image, type }`，`<meta property="og:*" />`에서 가져옵니다.                |
| `jsonLd`    | 항상                 | 모든 `<script type="application/ld+json">` 블록의 원시 JSON 배열입니다.                              |
| `llm`       | LLM이 실행되고 성공할 때    | `{ kind, data, model, promptCharCount }`，Zod로 검증된 유형화된 결과입니다.                            |
| `llmError`  | LLM이 실행되었으나 실패할 때  | `{ kind, error, model }`，요청이 중단되지 않으며, 휴리스틱 결과는 여전히 반환됩니다.                               |
| `amazon`    | URL이 `amazon.*`일 때 | 오래된 amazon 전용 크롤러 결과(점진적으로 폐기될 예정입니다).                                                   |

## schema.org 매핑기 범위

우선순위에 따라 정렬됨 (히트 시 `structured.schemaOrg.primary`로 사용됨):

| schema.org 유형                                                                                              | 매핑 종류     | 출력 필드                                                                                                                                                                                    |
| ---------------------------------------------------------------------------------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Product`                                                                                                  | product   | `name, sku, gtin, model, color, brand, url, images, offer.{price,currency,availability,condition,seller}, rating.{value,count}, reviews[], properties[]`                                 |
| `Recipe`                                                                                                   | recipe    | `name, description, image, datePublished, author, cookTime, prepTime, totalTime, recipeYield, ingredients[], instructions[], nutrition, rating, keywords, recipeCategory, recipeCuisine` |
| `VideoObject`                                                                                              | video     | `name, description, thumbnailUrl, uploadDate, duration, embedUrl, contentUrl, channel, interactionCount`                                                                                 |
| `JobPosting`                                                                                               | job       | `title, description, datePosted, validThrough, hiringOrganization, jobLocation, baseSalary, employmentType`                                                                              |
| `Event`（포함 `*Event`）                                                                                       | event     | `name, description, startDate, endDate, location.{name,address}, organizer, offer.{url,price,currency}`                                                                                  |
| `Article` / `NewsArticle` / `BlogPosting` / `ScholarlyArticle` / `TechArticle` / `Report` / `*NewsArticle` | article   | `subtype, headline, description, datePublished, dateModified, author, publisher, image[], url, sameAs[]`                                                                                 |
| `FAQPage`                                                                                                  | faq       | `questions[{question, answer}]`                                                                                                                                                          |
| `BreadcrumbList`                                                                                           | （형제에 연결됨） | 항상 `structured.schemaOrg.breadcrumbs[]`에 출력되며, primary로 사용되지 않습니다.                                                                                                                       |

매핑기 처리:

* `@graph` 컨테이너 (재귀적으로 전개됨)；
* `@type` 배열 (예: `["Recipe", "NewsArticle"]` —— 두 개 모두 인식되며, 우선순위에 따라 승리)；
* `http://schema.org/` 접두사 변형；
* 중첩된 `Offer` 및 `AggregateOffer` (후자는 `lowPrice`로 읽음)；
* 상대 이미지 URL (최종 URL에 따라 절대적으로 해석됨).

## LLM 유형화 스키마

`enable_llm: true` **그리고** schema.org에 primary가 없을 때, 추출기는 URL 휴리스틱
(또는 `expected_type` 힌트)에 따라 아래 중 하나의 Zod 스키마 검증 모델 출력을 선택합니다:

| 종류           | URL 휴리스틱                                                                           | 필수 필드      | 선택 필드                                                                                                                                                                        |
| ------------ | ---------------------------------------------------------------------------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `article`    | 텍스트 ≥400자이며 다른 항목이 미스일 때                                                           | `headline` | `description, byline, publishedAt, language, topics[], sections[{heading,summary}]`                                                                                          |
| `product`    | `amazon.* / ebay.* / aliexpress.* / temu.* / walmart.* / bestbuy.*`                | `name`     | `description, brand, sku, price, currency, availability, rating.{value,count}, bullets[], specifications[{name,value}]`                                                      |
| `discussion` | `news.ycombinator.com / reddit.com / lobste.rs`                                    | `title`    | `author, postedAt, points, commentCount, body, url`                                                                                                                          |
| `recipe`     | `allrecipes / foodnetwork / seriouseats / epicurious / bonappetit / simplyrecipes` | `name`     | `description, author, cookTime, prepTime, totalTime, recipeYield, ingredients[], instructions[], nutrition, rating, keywords[]`                                              |
| `video`      | `youtube.com/watch / youtu.be / vimeo.com/<id> / tiktok.com/@/video`               | `name`     | `description, channel, uploadDate, duration, viewCount, likeCount, thumbnailUrl, transcript`                                                                                 |
| `job`        | `greenhouse.io / lever.co / jobs.* / careers.* / workable.com / bamboohr`          | `title`    | `description, company, location, remote, employmentType, datePosted, validThrough, salaryMin, salaryMax, salaryCurrency, salaryPeriod, responsibilities[], qualifications[]` |

LLM 성공 시 최상위 필드에 "last-resort"로 다시 채워집니다:

* `article` → `description` / `byline` / `publishedAt` / `language`
* `product` → `description`
* `discussion` → `description`（= body의 처음 280자）/ `byline`（= author）/ `publishedAt`（= postedAt）
* `recipe` → `description` / `byline`（= author）
* `video` → `description` / `byline`（= channel）/ `publishedAt`（= uploadDate）
* `job` → `description` / `byline`（= company）/ `publishedAt`（= datePosted）

다시 채우기는 확정적인 데이터 소스가 **해당 필드를 채우지 않았을 때**만 발생합니다 — LLM은 항상 마지막 보루입니다.

## 캐시

동일한 요청은 동일한 Redis 키로 해시됩니다:
`webextrator:cache:extract:<sha256(canonical-json)>`。캐시 키는 **무시**합니다 `async`、
`bypass_cache`、`cache_ttl_seconds`（이는 작동 스위치이며, 응답에 영향을 미치지 않습니다）。`cookies` /
`headers` **는** 분리된 캐시로 저장됩니다.

| 필드                     | 효과                                                     |
| ---------------------- | ------------------------------------------------------ |
| `bypass_cache: true`   | 읽기를 건너뜁니다; 이번 결과는 여전히 캐시에 기록되며, 다음 동일한 요청이 적중할 수 있습니다. |
| `cache_ttl_seconds: 0` | 이번 응답은 **캐시되지 않습니다**.                                  |
| `cache_ttl_seconds: N` | 이 항목의 TTL을 사용자 정의합니다 (기본 3600초).                       |

캐시 적중 응답은 `data.cached: true` 및 `data.cacheStoredAt: <unix-ms>`를 포함합니다.

## 비동기 모드 및 콜백

`async: true`로 설정하면 비동기 모드로 전환됩니다 (또한 `callback_url`을 제공하면 자동으로 전환됩니다). 플랫폼은 즉시 반환합니다 (HTTP 200):

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

작업이 완료되면 전체 envelope을 `callback_url`로 `POST`합니다 (구성된 경우). 또한 나중에 [`/webextrator/tasks`](development_webextrator_tasks)에서 수동으로 조회할 수 있습니다.

## 예시

### 1. Wikipedia 기사 (schema.org 적중, LLM 필요 없음)

```bash theme={null}
curl -X POST https://api.acedata.cloud/webextrator/extract \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://en.wikipedia.org/wiki/Diffbot",
    "expected_type": "article"
  }'
```

`data.structured.schemaOrg.primary` 주요 필드:

```json theme={null}
{
  "kind": "article",
  "subtype": "Article",
  "headline": "미국의 기계 학습 및 지식 관리 회사",
  "datePublished": "2007-08-08T05:47:27Z",
  "dateModified": "2025-07-10T20:42:45Z",
  "author": { "name": "위키미디어 프로젝트 기여자", "type": "Organization" },
  "publisher": { "name": "위키미디어 재단, Inc." }
}
```

### 2. BestBuy 상품 페이지 (schema.org 적중)

```bash theme={null}
curl -X POST https://api.acedata.cloud/webextrator/extract \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://www.bestbuy.com/product/apple-airpods-pro-2nd-generation-white/JJ8ZH6TPSW",
    "expected_type": "product"
  }'
```

schema.org 추출:

```json theme={null}
{
  "kind": "product",
  "name": "Apple - 리퍼비시 우수 - AirPods Pro (2세대) - 화이트",
  "sku": "10845412",
  "model": "MQD83AM/A",
  "color": "화이트",
  "brand": "Apple",
  "offer": { "price": 159.99, "currency": "USD", "availability": "https://schema.org/InStock", "seller": "Best Buy" },
  "rating": { "value": 4.4, "count": 8 }
}
```

### 3. AllRecipes 레시피 페이지 (영양 및 단계 포함)

```bash theme={null}
curl -X POST https://api.acedata.cloud/webextrator/extract \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://www.allrecipes.com/recipe/16354/easy-meatloaf/"
  }'
```

schema.org 추출:

```json theme={null}
{
  "kind": "recipe",
  "name": "쉬운 미트로프",
  "cookTime": "PT60M",
  "totalTime": "PT75M",
  "recipeYield": "8 / 1 (9x5-inch) 미트로프",
  "ingredients": ["1 1/2 파운드 다진 소고기", "..."],
  "instructions": [{ "text": "오븐을 350°F로 예열하세요 ..." }, "..."],
  "rating": { "value": 4.7, "count": 9348 }
}
```

### 4. HN 토론 페이지 (JSON-LD 없음 — LLM 활성화 필요)

```bash theme={null}
curl -X POST https://api.acedata.cloud/webextrator/extract \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://news.ycombinator.com/item?id=37000000",
    "enable_llm": true
  }'
```

`data.structured.llm.data`:

```json theme={null}
{
  "kind": "discussion",
  "title": "Show HN: 웹 페이지를 추출하는 새로운 방법",
  "author": "alice",
  "points": 173,
  "commentCount": 42,
  "body": "안녕하세요 HN, 우리는 Diffbot의 Analyze API에 대한 자체 호스팅 대안을 만들었습니다 ..."
}
```

최상위 필드도 다시 채워짐: `byline = "alice"`、`publishedAt = "..."`。

### 5. Amazon 상품 페이지 (Amazon JSON-LD 없음 — LLM 활성화 필요)

```bash theme={null}
curl -X POST https://api.acedata.cloud/webextrator/extract \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://www.amazon.com/dp/B0BSHF7WHW",
    "expected_type": "product",
    "enable_llm": true
  }'
```

`data.structured.llm.data` (유형화 `product`):

```json theme={null}
{
  "kind": "product",
  "name": "Apple 2023 MacBook Pro M2 Pro 14인치",
  "brand": "Apple",
  "price": 1799,
  "currency": "USD",
  "bullets": ["Apple M2 Pro 칩, 10코어 CPU", "..."],
  "specifications": [{ "name": "디스플레이 크기", "value": "14.2 인치" }, "..."]
}
```

### Python (requests)

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

API_KEY = os.environ["ACEDATA_API_KEY"]

resp = requests.post(
    "https://api.acedata.cloud/webextrator/extract",
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json",
    },
    json={
        "url": "https://en.wikipedia.org/wiki/Diffbot",
        "expected_type": "article",
    },
    timeout=120,
)
resp.raise_for_status()
data = resp.json()["data"]

primary = (data.get("structured") or {}).get("schemaOrg", {}).get("primary")
print("contentType:", data["contentType"])
print("title:      ", data["title"])
print("byline:     ", data.get("byline"))
print("publishedAt:", data.get("publishedAt"))
if primary and primary["kind"] == "article":
    print("headline:    ", primary["headline"])
    print("dateModified:", primary.get("dateModified"))
```

### Node.js (fetch)

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

const res = await fetch('https://api.acedata.cloud/webextrator/extract', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    url: 'https://www.allrecipes.com/recipe/16354/easy-meatloaf/',
  }),
});
const { data } = await res.json();
const recipe = data?.structured?.schemaOrg?.primary;
console.log(recipe.name, recipe.cookTime, recipe.ingredients.length, '종류의 재료');
```

## 팁 및 주의사항

* **`expected_type`를 전달할 수 있으면 전달하세요.** 무료 팁으로, 휴리스틱 판단을 건너뛰고, URL 패턴이 내장 목록에 없는 페이지에 특히 유용합니다.
* **`enable_llm: true`는 schema.org 적중 페이지에서 무료입니다.** LLM은 schema.org에 primary가 없을 때만 호출되므로 기본적으로 켜두는 것이 안전합니다.
* **디버깅 시 `rawSignals.hasJsonLd`를 먼저 확인하세요.** 만약 `true`이지만 `structured.schemaOrg.primary`가 `null`이라면, 페이지가 우리가 매핑한 적이 없는 `@type`을 사용한 것입니다 — 이슈를 제기하면 추가하겠습니다.
* **`structured.llmError`는 정보성입니다.** 요청은 여전히 성공적이며, 휴리스틱 결과도 여전히 반환됩니다. `llmError.error`를 확인하여 원인을 파악하세요 (타임아웃, JSON 파싱 실패, Zod 검증 실패).
* **비기사 페이지의 `links[]`는 관련성 정렬을 하지 않습니다.** "최대 100개 + 유효하지 않은 프로토콜 필터링"에 따라 최선을 다해 정리합니다.
* **캐시 적중도 요금이 부과됩니다.** 캐시는 지연 및 브라우저 풀 보호를 위해 존재하며, 비용 절감을 위한 것이 아닙니다.
