# Batch และ crawl API: อ่านหลาย URL หรือทั้งเว็บไซต์

> API อ่านเว็บและครอว์ลแบบ batch: อ่านได้สูงสุด 100 URLs ต่อ job หรือทั้งเว็บสูงสุด 10,000 หน้า เคารพ robots.txt และ Crawl-delay ไม่ต้อง KYC จ่ายด้วยคริปโต

- URL: https://webreaderapi.com/th/docs/crawl/
- อัปเดตล่าสุด: 2026-10-22
- ผู้เผยแพร่: WebReaderAPI (https://webreaderapi.com)

มีสอง endpoint ที่เริ่ม background job: `/batch` สำหรับรายการสูงสุด 100 URLs, `/crawl` สำหรับหนึ่งเว็บไซต์ ทั้งคู่คืนค่า job ID; คุณเก็บหน้าเว็บจาก `/jobs/{job_id}` ทีละหน้าของผลลัพธ์

**คำตอบสั้น — จะ crawl เว็บไซต์เป็น Markdown ด้วย WebReaderAPI ได้อย่างไร?**

ส่ง start URL ไปที่ `POST https://webreaderapi.com/api/v1/crawl` พร้อมหน้า `limit` **WebReaderAPI** อ่านเว็บไซต์ในเบื้องหลัง เฉพาะโดเมนเดียวกัน ตาม sitemap, robots.txt และ Crawl-delay สูงสุด 10,000 หน้าต่อ job เก็บหน้า Markdown จาก job ทีละหน้าของผลลัพธ์ การอ่านแต่ละหน้าใช้ 1 เครดิต (2 เครดิตเมื่อใช้เบราว์เซอร์) เริ่มที่ US$0.07/1K หน้า ณ ตุลาคม 2026 ไม่ต้อง KYC จ่ายด้วยคริปโต

## Jobs โดยสรุป

| คุณสมบัติ | ค่า |
| --- | --- |
| Batch | สูงสุด 100 URL ต่อ batch job: `POST https://webreaderapi.com/api/v1/batch` |
| Crawl | crawl หนึ่งเว็บไซต์ต่อหนึ่งงาน สูงสุด 10,000 หน้า โดยทำตาม sitemap, robots.txt และ Crawl-delay ของเว็บนั้น: `POST https://webreaderapi.com/api/v1/crawl` |
| ผลลัพธ์ | `GET https://webreaderapi.com/api/v1/jobs/{job_id}` จากนั้น `?cursor=…` สำหรับหน้าถัดไปของผลลัพธ์ |
| การเรียกเก็บเงิน | การอ่านแต่ละหน้าคิดค่าบริการเหมือนการเรียก `/read`; หน้าที่ถูกข้ามโดย robots.txt และหน้าที่ล้มเหลวไม่คิดค่าใช้จ่าย |
| ขอบเขตของ crawl | หนึ่งโดเมน; ลิงก์ไปยังโดเมนอื่นจะแสดงในรายการแต่ไม่ถูกอ่าน |
| ยกเลิก job | ไม่มีใน v1: ตั้ง `limit` เป็นจำนวนหน้าสูงสุดที่คุณพร้อมจ่าย |

## เริ่ม batch job

`POST /api/v1/batch`

อ่านสูงสุด 100 URLs ในเบื้องหลังด้วยตัวเลือกเดียวกัน ตัวเลือกใช้ชื่อและค่าของ [/read](https://webreaderapi.com/th/docs/read/) คำตอบคือ job ID; ค่าเป็นเพียงตัวอย่าง

การยืนยันตัวตน: API key ใน header `Authorization: Bearer API_KEY`

**Request body**

| ฟิลด์ | ประเภท | จำเป็น | คำอธิบาย |
| --- | --- | --- | --- |
| `urls` | array of strings | ใช่ | URL http หรือ https จำนวน 1 ถึง 100 รายการ URL ที่ซ้ำกันจะถูกอ่านเพียงครั้งเดียว |
| `options` | object | ไม่ใช่ | พารามิเตอร์ `/read` ใดก็ได้ ยกเว้น `url`: `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
  }
}
```

**Response** `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);
```

## เริ่มงาน crawl

`POST /api/v1/crawl`

อ่านเว็บไซต์หนึ่งแห่งจาก URL เริ่มต้น แบบ breadth first โดย crawler จะอยู่ในโดเมนของ URL เริ่มต้นเท่านั้น ใช้ sitemap ที่ระบุใน robots.txt หรือที่พบที่ `/sitemap.xml` และเคารพ robots.txt รวมถึง Crawl-delay ค่าต่างๆ เป็นเพียงตัวอย่าง

การยืนยันตัวตน: API key ใน header `Authorization: Bearer API_KEY`

**Request body**

| ฟิลด์ | ประเภท | จำเป็น | คำอธิบาย |
| --- | --- | --- | --- |
| `url` | string | ใช่ | หน้าเริ่มต้น เช่น หน้าหลักของเว็บไซต์เอกสาร |
| `limit` | integer | ไม่ใช่ | จำนวนหน้าสูงสุดที่จะอ่าน สูงสุด 10,000 หน้า งานจะหยุดเมื่อถึงจำนวนนี้ |
| `max_depth` | integer | ไม่ใช่ | จำนวนขั้นของลิงก์สูงสุดที่นับห่างจาก URL เริ่มต้น |
| `include_paths` | array of strings | ไม่ใช่ | รูปแบบ path ง่ายๆ ที่ต้องการเก็บ เช่น `/docs/*` |
| `exclude_paths` | array of strings | ไม่ใช่ | รูปแบบ path ง่ายๆ ที่ต้องการข้าม เช่น `/blog/tag/*` |
| `options` | object | ไม่ใช่ | พารามิเตอร์ `/read` ใดก็ได้ ยกเว้น `url` ใช้กับทุกหน้า |

*ตัวอย่าง: 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"
  }
}
```

**Response** `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` จะเป็น response ของ `/read` แบบเต็ม หรือ `{url, error}` สำหรับหน้าที่อ่านไม่ได้ เมื่อมี `next_cursor` ให้เรียกอีกครั้งโดยตั้ง `cursor` เป็นค่านั้น เมื่อไม่มี แสดงว่ายังไม่มีผลลัพธ์เพิ่มที่พร้อม ค่าต่างๆ เป็นเพียงตัวอย่าง

การยืนยันตัวตน: API key ใน header `Authorization: Bearer API_KEY`

**พารามิเตอร์**

| ชื่อ | ตำแหน่ง (In) | ประเภท | จำเป็น | คำอธิบาย |
| --- | --- | --- | --- | --- |
| `job_id` | path | string | ใช่ | ID ที่คืนมาจาก `/batch` หรือ `/crawl` |
| `cursor` | query | string | ไม่ใช่ | ค่า `next_cursor` จากครั้งก่อนหน้า เพื่อรับผลลัพธ์ถัดไป |

**Response** `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);
```

## ฉันจะรอให้งานเสร็จได้อย่างไร?

poll `/jobs/{job_id}` และเก็บ cursor ไว้ ประมวลผลผลลัพธ์ที่ได้ แล้วเรียกอีกครั้งโดยตั้ง `cursor` เป็น `next_cursor` เมื่อไม่มี `next_cursor` และ `status` เป็น `done` แสดงว่าคุณได้ครบทั้งหมดแล้ว เมื่อไม่มีและงานยัง `running` อยู่ ให้รอแล้วถามอีกครั้งด้วย cursor เดิม

เริ่ม poll ทุก 2 วินาที และเพิ่มเวลาเป็นสองเท่าจนถึง 30 วินาที ตราบใดที่ยังไม่มีข้อมูลใหม่ การ crawl ที่เคารพ Crawl-delay ของไซต์อาจใช้เวลานาน ดังนั้นการรอนานเป็นเรื่องปกติและไม่มีค่าใช้จ่าย

*Python: poll งานและบันทึกทุกหน้าเป็นไฟล์ .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")

```

## crawl อ่านหน้าไหนบ้าง?

การ crawl เริ่มจาก URL ของคุณและขยายออกไปทีละขั้นของลิงก์ แบบ breadth first สูงสุดถึง `max_depth` นอกจากนี้ยังอ่าน sitemap ของไซต์ จากที่อยู่ที่ระบุใน robots.txt หรือจาก `/sitemap.xml` เพื่อค้นหาหน้าที่มีลิงก์ชี้ไปน้อย

- **เฉพาะโดเมนเดียวกันเท่านั้น** ลิงก์ไปยังโดเมนอื่นจะแสดงในผลลัพธ์เมื่อคุณขอ `include_links` แต่จะไม่ถูกอ่าน
- **ตัวกรอง path** `include_paths` และ `exclude_paths` รับรูปแบบง่ายๆ เช่น `/blog/*`
- **หนึ่งสำเนาต่อหน้า** เมื่อไซต์ประกาศ canonical URL หน้าที่เข้าถึงผ่านหลายที่อยู่จะถูกอ่านเพียงครั้งเดียว
- **หยุดแบบเด็ดขาด** งานจะจบเมื่ออ่านครบ `limit` หน้า

## crawl เคารพไซต์อย่างไร?

ทุกคำขอมี User-Agent `WebReaderAPI/1.0 (+https://webreaderapi.com/bot/)` และ crawler ปฏิบัติตามกฎ robots.txt สำหรับ `WebReaderAPI` หรือสำหรับ robot ทั้งหมดเมื่อไม่มีใครระบุชื่อ หน้าที่ไม่อนุญาตจะถูกข้ามและปรากฏใน `results` พร้อม error `blocked_by_robots` โดยไม่มีค่าใช้จ่าย

เมื่อ robots.txt ตั้งค่า `Crawl-delay` crawler จะรอตามเวลานั้นระหว่างคำขอสองครั้งไปยังไซต์ หากเว็บไซต์ประกาศ Content-Signal: ai-input=no ใน robots.txt ของมัน เราจะไม่อ่านหน้าของเว็บนั้นให้คุณ หากเว็บไซต์ปฏิเสธคำขอของเรา เราจะส่งคำตอบของเว็บนั้นกลับไป: เราไม่แก้ CAPTCHA ไม่เข้าสู่ระบบบัญชี และไม่พยายามหลบเลี่ยงการบล็อกหรือ paywall

## การ crawl มีค่าใช้จ่ายเท่าไร?

การอ่านแต่ละหน้าคิดค่าเท่ากับการเรียก `/read`: 1 เครดิต 2 เครดิตหากหน้าต้องใช้เบราว์เซอร์ บวก 10 เครดิตหากใช้ `country` หน้าที่ถูกข้ามและล้มเหลวไม่มีค่าใช้จ่าย `credits_used` ของงานจะรวมยอดที่เรียกเก็บไปแล้ว

เว็บไซต์เอกสาร 500 หน้าอ่านโดยไม่ใช้เบราว์เซอร์ใช้ประมาณ 500 เครดิต ซึ่งแพ็ก 10,000 หน้า (US$3.75) ครอบคลุมได้หลายรอบ เนื่องจากงานไม่สามารถยกเลิกได้ใน v1 `limit` จึงเป็นเพดานการใช้จ่ายของคุณ: ตั้งให้เป็นจำนวนหน้าสูงสุดที่คุณพร้อมจะจ่าย

## Batch และ crawl: คำถามที่พบบ่อย

### ควรใช้ /batch หรือเรียก /read หลายครั้ง?

ใช้ `/batch` เมื่อคุณมี URL อยู่แล้วและไม่ต้องการคำตอบแต่ละอันทันที ช่วยให้คุณไม่ต้องจัดการ concurrency และ retries เอง ใช้ `/read` เมื่อ agent ต้องการหนึ่งหน้าตอนนี้

### ผลลัพธ์ของงานเก็บไว้นานแค่ไหน?

เก็บผลลัพธ์ทันทีที่พร้อม ผลลัพธ์จะรอจนกว่าคุณจะดึงไป เมื่อส่งมอบแล้วจะไม่เก็บสำเนาเนื้อหาหน้าไว้ ดังนั้นจงเก็บสิ่งที่คุณต้องการไว้ฝั่งของคุณเอง

### crawl ตามลิงก์ไปยังเว็บไซต์อื่นได้หรือไม่?

ไม่ได้ crawl อ่านเพียงโดเมนเดียว โดเมนอื่นจะปรากฏเป็นลิงก์เมื่อคุณขอ และคุณสามารถส่ง URL เหล่านั้นไปยัง `/batch` หากต้องการให้อ่าน

### หยุด crawl ที่เริ่มแล้วได้หรือไม่?

ไม่ได้ใน v1 งานจะหยุดที่ `limit` หน้า ดังนั้นจงเลือกขีดจำกัดที่ตรงกับงบประมาณของคุณ หน้าที่อ่านแล้วจะยังคงถูกเรียกเก็บ หน้าที่ไม่เคยถูกอ่านไม่มีค่าใช้จ่าย

---

WebReaderAPI: WebReaderAPI คือ API อ่านหน้าเว็บสำหรับ AI agent ที่แปลงหน้าเว็บสาธารณะใด ๆ เป็น Markdown สะอาด จ่ายด้วยคริปโตโดยไม่ต้องอัปโหลดบัตรประชาชน เครดิตแบบเติมเงินเริ่มต้นที่ US$0.07/1K หน้า ไม่มีวันหมดอายุ และเคารพ robots.txt ไม่ต้อง KYC — ไม่ต้องใช้บัตรประชาชน ไม่ต้องเซลฟี่ ไม่ต้องเอกสาร จ่ายด้วยคริปโต
