Câu trả lời nhanh
Làm cách nào để crawl một trang web sang Markdown với WebReaderAPI?
Gửi URL bắt đầu tới POST https://webreaderapi.com/api/v1/crawl với một limit trang. WebReaderAPI đọc trang web ở chế độ nền, chỉ trong cùng domain, theo sitemap, robots.txt và Crawl-delay của trang, tối đa 10.000 trang mỗi job. Thu thập các trang Markdown từ job, mỗi lần một trang kết quả. Mỗi trang đọc tốn 1 credit (2 nếu dùng trình duyệt), từ 0,07 US$/1K trang tính đến tháng 10 năm 2026. Không cần KYC; thanh toán bằng crypto.
Tổng quan về jobs
| Batch | Tối đa 100 URL mỗi batch job: POST https://webreaderapi.com/api/v1/batch |
|---|---|
| Crawl | Crawl một website mỗi job, tối đa 10.000 trang, theo sitemap, robots.txt và Crawl-delay của trang: POST https://webreaderapi.com/api/v1/crawl |
| Kết quả | GET https://webreaderapi.com/api/v1/jobs/{job_id}, sau đó ?cursor=… để lấy trang kết quả tiếp theo |
| Tính phí | Mỗi trang đọc được tính phí như một lệnh gọi /read; các trang bị robots.txt bỏ qua và các trang thất bại không tốn phí |
| Phạm vi của một lần crawl | Một domain; các liên kết tới domain khác được liệt kê nhưng không được đọc |
| Hủy job | Không có trong v1: đặt limit bằng số trang tối đa bạn sẵn sàng trả tiền |
Khởi chạy job batch
POST/api/v1/batch
Đọc tới 100 URL ở chế độ nền với cùng các tùy chọn. Tùy chọn dùng tên và giá trị của /read. Câu trả lời là job ID; các giá trị chỉ mang tính minh họa.
Xác thực: khóa API trong header Authorization: Bearer API_KEY.
Request body
| Field | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
urls | array of strings | Có | 1 đến 100 URL http hoặc https. URL trùng lặp chỉ được đọc một lần. |
options | object | Không | Bất kỳ tham số /read nào ngoại trừ url: format, main_content, render, include_links, include_images, country, max_chars, timeout. |
{
"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
{
"job_id": "job_5c1e8a90d2"
}Ví dụ code
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"}}'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"])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);Bắt đầu một job crawl
POST/api/v1/crawl
Đọc một trang web từ URL bắt đầu, theo chiều rộng. Trình thu thập dữ liệu ở lại trên domain của URL bắt đầu, dùng sitemap được nêu trong robots.txt hoặc tìm thấy tại /sitemap.xml, và tuân thủ robots.txt cùng Crawl-delay. Các giá trị chỉ mang tính minh họa.
Xác thực: khóa API trong header Authorization: Bearer API_KEY.
Request body
| Field | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
url | string | Có | Trang bắt đầu, chẳng hạn như trang chủ của một site tài liệu. |
limit | integer | Không | Số trang tối đa cần đọc, tối đa 10.000. Job dừng khi đạt số này. |
max_depth | integer | Không | Số bước liên kết tối đa tính từ URL bắt đầu. |
include_paths | array of strings | Không | Các mẫu đường dẫn đơn giản cần giữ, chẳng hạn như /docs/*. |
exclude_paths | array of strings | Không | Các mẫu đường dẫn đơn giản cần bỏ qua, chẳng hạn như /blog/tag/*. |
options | object | Không | Bất kỳ tham số /read nào ngoại trừ url, áp dụng cho mọi trang. |
{
"url": "https://docs.example.com/",
"limit": 500,
"max_depth": 3,
"include_paths": [
"/docs/*"
],
"exclude_paths": [
"/docs/archive/*"
],
"options": {
"format": "markdown"
}
}Response 202 Accepted
{
"job_id": "job_8b27f41c0e"
}Ví dụ code
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/*"]}'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"])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);Lấy trạng thái và kết quả của job
GET/api/v1/jobs/{job_id}
Trả về trạng thái của job và các kết quả tiếp theo đã sẵn sàng. Mỗi mục trong results hoặc là một phản hồi /read đầy đủ, hoặc là {url, error} cho một trang không đọc được. Khi có next_cursor, hãy gọi lại với cursor được đặt bằng giá trị đó; khi không có, chưa có thêm kết quả nào sẵn sàng. Các giá trị chỉ mang tính minh họa.
Xác thực: khóa API trong header Authorization: Bearer API_KEY.
Tham số
| Tên | Vị trí | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|---|
job_id | path | string | Có | ID được trả về bởi /batch hoặc /crawl. |
cursor | query | string | Không | Giá trị next_cursor từ lần gọi trước, để lấy các kết quả tiếp theo. |
Response 200
{
"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
}Ví dụ code
curl -sG "https://webreaderapi.com/api/v1/jobs/job_8b27f41c0e" \
-H "Authorization: Bearer $WEBREADER_API_KEY" \
-d cursor=c_000002import 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"))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);Làm sao để chờ một job hoàn tất?
Poll /jobs/{job_id} và giữ cursor. Xử lý các kết quả nhận được, rồi gọi lại với cursor được đặt bằng next_cursor. Khi không có next_cursor và status là done, bạn đã có tất cả; khi không có nó và job vẫn running, hãy chờ và hỏi lại với cùng cursor.
Bắt đầu poll mỗi 2 giây và tăng gấp đôi thời gian chờ lên tối đa 30 giây khi không có gì mới. Một crawl tuân thủ Crawl-delay của site có thể mất khá lâu, nên chờ lâu là bình thường và không tốn gì.
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")
Một crawl đọc những trang nào?
Một crawl bắt đầu từ URL của bạn và lan ra ngoài từng bước liên kết một, theo chiều rộng, tối đa max_depth. Nó cũng đọc sitemap của site, từ địa chỉ được nêu trong robots.txt hoặc từ /sitemap.xml, để tìm những trang ít được liên kết tới.
- Chỉ cùng domain. Liên kết tới domain khác được liệt kê trong kết quả khi bạn yêu cầu
include_links, nhưng không được đọc. - Bộ lọc đường dẫn.
include_pathsvàexclude_pathsnhận các mẫu đơn giản như/blog/*. - Một bản sao cho mỗi trang. Khi một site khai báo canonical URL, một trang được truy cập qua nhiều địa chỉ chỉ được đọc một lần.
- Điểm dừng cứng. Job kết thúc khi đã đọc
limittrang.
Một crawl tôn trọng site như thế nào?
Mỗi request đều mang User-Agent WebReaderAPI/1.0 (+https://webreaderapi.com/bot/), và trình thu thập dữ liệu tuân theo quy tắc robots.txt cho WebReaderAPI, hoặc cho tất cả robot khi không có robot nào được nêu tên. Các trang bị disallow sẽ bị bỏ qua và xuất hiện trong results với lỗi blocked_by_robots; chúng không tốn gì.
Khi robots.txt đặt Crawl-delay, trình thu thập dữ liệu chờ chừng đó giữa hai request tới site. Nếu một website khai báo Content-Signal: ai-input=no trong robots.txt, chúng tôi không đọc các trang của trang đó cho bạn. Nếu một website từ chối yêu cầu của chúng tôi, chúng tôi trả lại câu trả lời của trang đó: chúng tôi không bao giờ giải CAPTCHA, đăng nhập vào tài khoản, hay tìm cách vượt qua chặn hoặc paywall.
Một crawl tốn bao nhiêu?
Mỗi trang được đọc tốn bằng một lần gọi /read: 1 credit, 2 nếu trang cần trình duyệt, cộng thêm 10 với country. Trang bị bỏ qua và thất bại miễn phí. credits_used của job cộng dồn những gì đã được tính phí tới thời điểm hiện tại.
Một site tài liệu 500 trang đọc không cần trình duyệt dùng khoảng 500 credit, mà pack 10.000 trang (3,75 US$) đủ trả nhiều lần. Vì job không thể hủy trong v1, limit là mức trần chi tiêu của bạn: hãy đặt nó bằng số trang nhiều nhất bạn sẵn sàng trả tiền.
Batch và crawl: các câu hỏi thường gặp
Tôi nên dùng /batch hay nhiều lần gọi /read?
Dùng /batch khi bạn đã có sẵn các URL và không cần từng câu trả lời ngay lập tức. Nó giúp bạn khỏi phải quản lý concurrency và retry. Dùng /read khi một agent cần một trang ngay bây giờ.
Kết quả của job được giữ bao lâu?
Hãy thu thập chúng ngay khi sẵn sàng. Kết quả chờ cho đến khi bạn fetch chúng; một khi đã giao, không giữ bản sao nào của nội dung trang, nên hãy lưu những gì bạn cần ở phía mình.
Một crawl có thể theo liên kết tới các trang web khác không?
Không. Một crawl chỉ đọc một domain. Các domain khác xuất hiện dưới dạng liên kết khi bạn yêu cầu, và bạn có thể gửi các URL đó tới /batch nếu muốn chúng được đọc.
Tôi có thể dừng một crawl đã bắt đầu không?
Không trong v1. Job dừng ở limit trang, nên hãy chọn giới hạn phù hợp với ngân sách của bạn. Các trang đã đọc vẫn bị tính phí; các trang chưa bao giờ đọc không tốn gì.