# Batch 및 crawl API: 여러 URL 또는 사이트 전체 읽기

> Batch 및 crawl API 레퍼런스: 작업당 최대 100개 URL 또는 최대 10,000페이지의 사이트 전체를 읽고, robots.txt와 Crawl-delay를 준수합니다. KYC 없이, 암호화폐 결제.

- URL: https://webreaderapi.com/ko/docs/crawl/
- 마지막 업데이트: 2026-10-22
- 발행자: WebReaderAPI (https://webreaderapi.com)

두 endpoint가 백그라운드 작업을 시작합니다: `/batch`은 최대 100개 URL 목록용, `/crawl`은 하나의 웹사이트용입니다. 둘 다 작업 ID를 반환하며, `/jobs/{job_id}`에서 한 번에 한 페이지씩 결과를 수집합니다.

**빠른 답변 — WebReaderAPI로 웹사이트를 Markdown으로 크롤링하려면 어떻게 하나요?**

시작 URL을 페이지 `limit`와 함께 `POST https://webreaderapi.com/api/v1/crawl`로 보내세요. **WebReaderAPI**은 백그라운드에서 사이트를 읽으며, 같은 도메인만, sitemap, robots.txt 및 Crawl-delay를 따라 작업당 최대 10,000페이지까지 처리합니다. 작업에서 Markdown 페이지를 한 번에 한 페이지씩 수집하세요. 페이지 읽기마다 1크레딧(브라우저 사용 시 2크레딧)이 들며, 2026년 10월 기준 US$0.07/1K 페이지입니다. KYC 없이 암호화폐 결제.

## 작업 한눈에 보기

| 속성 | 값 |
| --- | --- |
| Batch | 배치 작업당 최대 100개의 URL: `POST https://webreaderapi.com/api/v1/batch` |
| 크롤링 | 작업당 웹사이트 하나를 크롤링하며, 최대 10,000페이지까지 사이트맵, robots.txt 및 Crawl-delay를 따릅니다: `POST https://webreaderapi.com/api/v1/crawl` |
| 결과 | `GET https://webreaderapi.com/api/v1/jobs/{job_id}`, 다음 결과 페이지는 `?cursor=…` |
| 과금 | 페이지 읽기마다 `/read` 호출과 동일하게 과금됩니다; robots.txt에서 건너뛴 페이지와 실패한 페이지는 무료입니다 |
| 크롤링 범위 | 하나의 도메인; 다른 도메인으로의 링크는 나열되지만 읽지 않습니다 |
| 작업 취소 | v1에서는 제공되지 않습니다: 지불할 준비가 된 최대 페이지 수를 `limit`에 설정하세요 |

## Batch 작업 시작

`POST /api/v1/batch`

동일한 옵션으로 최대 100개 URL을 백그라운드에서 읽습니다. 옵션은 [/read](https://webreaderapi.com/ko/docs/read/)의 이름과 값을 사용합니다. 응답은 작업 ID이며, 값은 예시입니다.

인증: `Authorization: Bearer API_KEY` 헤더에 API 키를 넣습니다.

**요청 본문**

| 필드 | 타입 | 필수 | 설명 |
| --- | --- | --- | --- |
| `urls` | array of strings | 예 | http 또는 https URL 1~100개. 중복된 URL은 한 번만 읽습니다. |
| `options` | object | 아니요 | `url`을 제외한 모든 `/read` 파라미터: `format`, `main_content`, `render`, `include_links`, `include_images`, `country`, `max_chars`, `timeout`. |

*예시: three articles as Markdown*

```json
{
  "urls": [
    "https://example.com/blog/post-1",
    "https://example.com/blog/post-2",
    "https://example.org/news/launch"
  ],
  "options": {
    "format": "markdown",
    "max_chars": 50000
  }
}
```

**응답** `202 Accepted`

```json
{
  "job_id": "job_5c1e8a90d2"
}
```

*curl*

```bash
curl -s "https://webreaderapi.com/api/v1/batch" \
  -H "Authorization: Bearer $WEBREADER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://example.com/blog/post-1", "https://example.com/blog/post-2"], "options": {"format": "markdown"}}'
```

*Python*

```python
import os
import requests

resp = requests.post(
    "https://webreaderapi.com/api/v1/batch",
    headers={"Authorization": "Bearer " + os.environ["WEBREADER_API_KEY"]},
    json={"urls": ["https://example.com/blog/post-1", "https://example.com/blog/post-2"], "options": {"format": "markdown"}},
    timeout=30,
)
print(resp.json()["job_id"])
```

*Node.js*

```javascript
const res = await fetch('https://webreaderapi.com/api/v1/batch', {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.WEBREADER_API_KEY}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({ urls: ['https://example.com/blog/post-1', 'https://example.com/blog/post-2'], options: { format: 'markdown' } }),
});
console.log((await res.json()).job_id);
```

## 크롤링 작업 시작

`POST /api/v1/crawl`

시작 URL에서 한 웹사이트를 너비 우선 방식으로 읽습니다. 크롤러는 시작 URL의 도메인에 머물고, robots.txt에 명시되었거나 `/sitemap.xml`에서 발견된 sitemap을 사용하며, robots.txt와 Crawl-delay를 준수합니다. 값은 예시입니다.

인증: `Authorization: Bearer API_KEY` 헤더에 API 키를 넣습니다.

**요청 본문**

| 필드 | 타입 | 필수 | 설명 |
| --- | --- | --- | --- |
| `url` | string | 예 | 시작 페이지입니다. 예를 들어 문서 사이트의 홈페이지입니다. |
| `limit` | integer | 아니요 | 읽을 최대 페이지 수이며 최대 10,000까지입니다. 작업은 이 수에 도달하면 중지됩니다. |
| `max_depth` | integer | 아니요 | 시작 URL에서 떨어진 최대 링크 단계 수입니다. |
| `include_paths` | array of strings | 아니요 | 유지할 단순 경로 패턴입니다. 예: `/docs/*`. |
| `exclude_paths` | array of strings | 아니요 | 건너뛸 단순 경로 패턴입니다. 예: `/blog/tag/*`. |
| `options` | object | 아니요 | `url`을 제외한 모든 `/read` 파라미터로, 모든 페이지에 적용됩니다. |

*예시: the docs section of a site, 500 pages at most*

```json
{
  "url": "https://docs.example.com/",
  "limit": 500,
  "max_depth": 3,
  "include_paths": [
    "/docs/*"
  ],
  "exclude_paths": [
    "/docs/archive/*"
  ],
  "options": {
    "format": "markdown"
  }
}
```

**응답** `202 Accepted`

```json
{
  "job_id": "job_8b27f41c0e"
}
```

*curl*

```bash
curl -s "https://webreaderapi.com/api/v1/crawl" \
  -H "Authorization: Bearer $WEBREADER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://docs.example.com/", "limit": 500, "include_paths": ["/docs/*"]}'
```

*Python*

```python
import os
import requests

resp = requests.post(
    "https://webreaderapi.com/api/v1/crawl",
    headers={"Authorization": "Bearer " + os.environ["WEBREADER_API_KEY"]},
    json={"url": "https://docs.example.com/", "limit": 500, "include_paths": ["/docs/*"]},
    timeout=30,
)
print(resp.json()["job_id"])
```

*Node.js*

```javascript
const res = await fetch('https://webreaderapi.com/api/v1/crawl', {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.WEBREADER_API_KEY}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({ url: 'https://docs.example.com/', limit: 500, include_paths: ['/docs/*'] }),
});
console.log((await res.json()).job_id);
```

## 작업 상태와 결과 가져오기

`GET /api/v1/jobs/{job_id}`

작업의 상태와 준비된 다음 결과를 반환합니다. `results`의 각 항목은 전체 `/read` 응답이거나 읽을 수 없는 페이지에 대한 `{url, error}`입니다. `next_cursor`이 있으면 `cursor`를 해당 값으로 설정해 다시 호출하세요. 없으면 아직 준비된 결과가 더 없다는 뜻입니다. 값은 예시입니다.

인증: `Authorization: Bearer API_KEY` 헤더에 API 키를 넣습니다.

**파라미터**

| 이름 | 위치 | 타입 | 필수 | 설명 |
| --- | --- | --- | --- | --- |
| `job_id` | path | string | 예 | `/batch` 또는 `/crawl`이 반환한 ID입니다. |
| `cursor` | query | string | 아니요 | 다음 결과를 받기 위해 이전 호출에서 받은 `next_cursor` 값입니다. |

**응답** `200`

```json
{
  "job_id": "job_8b27f41c0e",
  "status": "running",
  "results": [
    {
      "url": "https://docs.example.com/docs/install",
      "final_url": "https://docs.example.com/docs/install",
      "status_code": 200,
      "rendered": false,
      "credits_used": 1,
      "title": "Install",
      "markdown": "# Install\n\nRun the installer...",
      "word_count": 418
    },
    {
      "url": "https://docs.example.com/docs/private",
      "error": {
        "code": "blocked_by_robots",
        "message": "robots.txt disallows this page for WebReaderAPI."
      }
    }
  ],
  "next_cursor": "c_000002",
  "credits_used": 1
}
```

*curl*

```bash
curl -sG "https://webreaderapi.com/api/v1/jobs/job_8b27f41c0e" \
  -H "Authorization: Bearer $WEBREADER_API_KEY" \
  -d cursor=c_000002
```

*Python*

```python
import os
import requests

resp = requests.get(
    "https://webreaderapi.com/api/v1/jobs/job_8b27f41c0e",
    headers={"Authorization": "Bearer " + os.environ["WEBREADER_API_KEY"]},
    params={"cursor": "c_000002"},
    timeout=30,
)
job = resp.json()
print(job["status"], len(job["results"]), job.get("next_cursor"))
```

*Node.js*

```javascript
const res = await fetch('https://webreaderapi.com/api/v1/jobs/job_8b27f41c0e?cursor=c_000002', {
  headers: { Authorization: `Bearer ${process.env.WEBREADER_API_KEY}` },
});
const job = await res.json();
console.log(job.status, job.results.length, job.next_cursor);
```

## 작업이 끝날 때까지 어떻게 기다리나요?

`/jobs/{job_id}`를 폴링하고 커서를 보관하세요. 받은 결과를 처리한 다음 `cursor`를 `next_cursor` 값으로 설정해 다시 호출합니다. `next_cursor`가 없고 `status`가 `done`이면 모든 결과를 받은 것이고, `next_cursor`가 없는데 작업이 아직 `running` 상태라면 잠시 기다린 뒤 같은 커서로 다시 요청하세요.

처음에는 2초마다 폴링하고, 새 결과가 없는 동안에는 대기 시간을 최대 30초까지 두 배씩 늘리세요. 사이트의 Crawl-delay를 지키는 크롤링은 시간이 걸릴 수 있으므로 오래 기다리는 것은 정상이며 비용이 들지 않습니다.

*Python: 작업을 폴링하고 모든 페이지를 .md 파일로 저장*

```python
import os
import pathlib
import re
import time

import requests

JOBS = "https://webreaderapi.com/api/v1/jobs"
HEADERS = {"Authorization": "Bearer " + os.environ["WEBREADER_API_KEY"]}


def save_job(job_id, folder="pages"):
    out = pathlib.Path(folder)
    out.mkdir(exist_ok=True)
    cursor, wait, saved = None, 2, 0
    while True:
        params = {"cursor": cursor} if cursor else {}
        job = requests.get(f"{JOBS}/{job_id}", headers=HEADERS, params=params, timeout=30).json()
        for item in job["results"]:
            if "error" in item:
                print("skipped", item["url"], item["error"]["code"])
                continue
            name = re.sub(r"[^a-z0-9]+", "-", item["final_url"].lower()).strip("-")[:120]
            (out / f"{name}.md").write_text(item["markdown"], encoding="utf-8")
            saved += 1
        if job.get("next_cursor"):
            cursor, wait = job["next_cursor"], 2
            continue
        if job["status"] == "done":
            return saved
        time.sleep(wait)
        wait = min(wait * 2, 30)


if __name__ == "__main__":
    print(save_job("job_8b27f41c0e"), "pages saved")

```

## 크롤링은 어떤 페이지를 읽나요?

크롤링은 지정한 URL에서 시작해 한 번에 링크 한 단계씩, 너비 우선(breadth first) 방식으로 최대 `max_depth`까지 바깥으로 확장됩니다. 또한 robots.txt에 명시된 주소나 `/sitemap.xml`에서 사이트의 sitemap을 읽어 링크가 거의 연결되지 않은 페이지도 찾아냅니다.

- **동일 도메인만.** 다른 도메인으로 향하는 링크는 `include_links`를 요청할 때 결과에 나열되지만, 읽지는 않습니다.
- **경로 필터.** `include_paths`과 `exclude_paths`는 `/blog/*` 같은 단순한 패턴을 받습니다.
- **페이지당 한 번만.** 사이트가 canonical URL을 선언하면 여러 주소로 접근되는 페이지는 한 번만 읽습니다.
- **확실한 종료 조건.** 작업은 `limit`개의 페이지를 읽으면 종료됩니다.

## 크롤링은 사이트를 어떻게 존중하나요?

모든 요청에는 User-Agent `WebReaderAPI/1.0 (+https://webreaderapi.com/bot/)`가 함께 전송되며, 크롤러는 `WebReaderAPI`에 대한 robots.txt 규칙을 따르고, 해당 이름을 명시한 규칙이 없으면 모든 로봇에 대한 규칙을 따릅니다. 허용되지 않은 페이지는 건너뛰며 `results`에 오류 `blocked_by_robots`과 함께 나타나고, 크레딧이 차감되지 않습니다.

robots.txt에 `Crawl-delay`이 설정되어 있으면 크롤러는 해당 사이트에 요청을 보낼 때마다 그 시간만큼 기다립니다. 사이트가 robots.txt에 Content-Signal: ai-input=no를 선언하면, 해당 페이지를 대신 읽지 않습니다. 사이트가 우리 요청을 거부하면 그 응답을 그대로 반환합니다. 우리는 CAPTCHA를 풀거나, 계정에 로그인하거나, 차단이나 페이월을 우회하려 시도하지 않습니다.

## 크롤링 비용은 얼마인가요?

페이지를 읽을 때마다 `/read` 호출과 같은 비용이 듭니다. 즉 1 크레딧이고, 페이지에 브라우저가 필요했다면 2 크레딧이며, `country`을 사용하면 10 크레딧이 추가됩니다. 건너뛴 페이지와 실패한 페이지는 무료입니다. 작업의 `credits_used`에는 지금까지 청구된 금액이 합산됩니다.

브라우저 없이 500페이지 분량의 문서 사이트를 읽으면 약 500 크레딧을 사용하며, 10,000페이지 팩(US$3.75)으로 여러 번 충당할 수 있습니다. v1에서는 작업을 취소할 수 없으므로 `limit`가 지출 한도입니다. 지불할 의사가 있는 최대 페이지 수로 설정하세요.

## 배치와 크롤링: 자주 묻는 질문

### /batch를 사용해야 하나요, 아니면 /read를 여러 번 호출해야 하나요?

이미 URL이 있고 각 응답을 즉시 받을 필요가 없다면 `/batch`을 사용하세요. 동시성과 재시도를 직접 관리할 필요가 없습니다. 에이전트가 지금 바로 한 페이지를 필요로 한다면 `/read`을 사용하세요.

### 작업 결과는 얼마나 보관되나요?

준비되는 즉시 결과를 수집하세요. 결과는 가져올 때까지 대기하며, 전달된 후에는 페이지 콘텐츠의 사본이 보관되지 않으므로 필요한 내용은 직접 저장하세요.

### 크롤링이 다른 웹사이트로 연결되는 링크를 따라갈 수 있나요?

아니요. 크롤링은 하나의 도메인만 읽습니다. 다른 도메인은 요청할 경우 링크로 나타나며, 해당 URL을 읽고 싶다면 `/batch`으로 보낼 수 있습니다.

### 시작된 크롤링을 중지할 수 있나요?

v1에서는 할 수 없습니다. 작업은 `limit`페이지에서 멈추므로 예산에 맞는 한도를 선택하세요. 이미 읽은 페이지는 계속 청구되며, 읽지 않은 페이지는 무료입니다.

---

WebReaderAPI: WebReaderAPI는 AI 에이전트를 위한 웹 리더 API로, 모든 공개 웹페이지를 깔끔한 Markdown으로 변환하며 신분증 업로드 없이 암호화폐로 결제합니다. US$0.07/1K 페이지부터 시작하는 선불 크레딧은 만료되지 않으며, robots.txt를 준수합니다. KYC 없음 — 신분증도, 셀피도, 서류도 없습니다. 암호화폐로 결제하세요.
