# Lỗi, từ chối do robots.txt và thử lại

> Mọi mã lỗi của WebReaderAPI: từ chối do robots.txt, từ trang web, hết thời gian chờ và lỗi credit, loại nào nên thử lại và tại sao không loại nào bị tính phí.

- URL: https://webreaderapi.com/vi/docs/errors/
- Cập nhật lần cuối: 2026-10-22
- Đơn vị phát hành: WebReaderAPI (https://webreaderapi.com)

Mỗi lỗi có một HTTP status, một `code` ổn định và một `message` dễ đọc. Không loại nào dùng credit. Một số đáng để thử lại; các từ chối đến từ phía trang web là cuối cùng, và chúng tôi không bao giờ lách qua chúng.

**Câu trả lời nhanh — Các mã lỗi của WebReaderAPI có nghĩa là gì, và những loại nào nên thử lại?**

Các lỗi **WebReaderAPI** chia thành ba nhóm, và không loại nào bao giờ bị tính phí. Vấn đề ở request (`invalid_url`, `invalid_api_key`, `insufficient_credits`) cần bạn sửa phía mình. Quyết định từ phía trang web (`blocked_by_robots`, `blocked_by_site_policy`, `target_refused`) là cuối cùng. Lỗi tạm thời (`fetch_failed`, `fetch_timeout`, `concurrency_limit`) có thể thử lại với backoff. Credit bắt đầu từ 0,07 US$/1K trang, thanh toán bằng crypto không cần KYC.

## API có thể trả về những mã lỗi nào?

Body luôn là `{"error": {"code", "message"}}`, cộng thêm `target_status` khi từ chối đến từ phía trang web.

| HTTP | `code` | Nguyên nhân | Thử lại? |
| --- | --- | --- | --- |
| 400 | `invalid_url` | Không phải http hoặc https, quá dài, hoặc là địa chỉ riêng tư, cục bộ hay metadata cloud | Không: sửa URL |
| 400 | `invalid_parameter` | Một tham số có giá trị nằm ngoài danh sách hoặc khoảng cho phép | Không: sửa yêu cầu |
| 401 | `invalid_api_key` | Khóa bị thiếu, sai hoặc đã bị thu hồi | Không: kiểm tra khóa |
| 402 | `insufficient_credits` | Số dư của bạn không còn credit nào | Sau khi mua một pack |
| 403 | `blocked_by_robots` | robots.txt của trang web không cho phép WebReaderAPI hoặc tất cả các robot truy cập trang | Không: kết thúc |
| 403 | `blocked_by_site_policy` | Trang web khai báo `Content-Signal: ai-input=no` | Không: kết thúc |
| 422 | `unsupported_content` | Một loại tệp chúng tôi không đọc (video, tệp nén) hoặc tệp quá lớn | Không |
| 429 | `concurrency_limit` | Bạn có quá nhiều yêu cầu đang chạy cùng lúc | Có, với backoff |
| 502 | `target_refused` | Trang web từ chối: 403, 429, trang challenge hoặc tường đăng nhập; trạng thái của nó nằm trong `target_status` | Không: kết thúc |
| 502 | `fetch_failed` | Lỗi DNS, TLS hoặc kết nối, hoặc trang web trả về 404 hay 410 | Một hoặc hai lần, với backoff |
| 504 | `fetch_timeout` | Trang web không trả lời trong `timeout` | Có, với backoff hoặc timeout dài hơn |

## Vì sao robots.txt và việc trang web từ chối là kết thúc?

Vì đó là quyết định của chủ trang web, và WebReaderAPI được xây dựng để tuân theo quyết định đó. Chúng tôi tôn trọng robots.txt: crawler của chúng tôi tự nhận là WebReaderAPI, tuân theo các quy tắc dành cho nó hoặc cho tất cả robot, và không đọc trang mà website không cho phép. Trang bị từ chối không bị tính phí. 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.

Khi một trang web từ chối yêu cầu theo cách khác, bằng 403, 429, trang challenge hoặc biểu mẫu đăng nhập, bạn nhận được `target_refused` và trạng thái của chính trang web trong `target_status`. 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. Thử lại cùng URL sau một phút thường sẽ nhận được kết quả y hệt, nên hãy coi các mã này là kết quả, không phải lỗi: ghi log rồi chuyển tiếp.

## Làm sao tôi kiểm tra lý do một trang bị từ chối?

Mở `/robots.txt` của trang web và tìm một nhóm nêu tên `WebReaderAPI`, hoặc nhóm `*` áp dụng cho tất cả các robot. Một dòng `Disallow` khớp với đường dẫn giải thích một `blocked_by_robots`. Một dòng như `Content-Signal: ai-input=no` giải thích một `blocked_by_site_policy`.

Chúng tôi lưu một bản sao của mỗi robots.txt tối đa 24 giờ. Nếu một trang web vừa mở một mục cho robot, thay đổi có thể mất đến một ngày mới áp dụng. Chủ trang web có thể đọc cách trình thu thập của chúng tôi hoạt động, và cách cho phép hoặc chặn nó, trên [trang trình thu thập của chúng tôi](https://webreaderapi.com/vi/bot/).

## Tôi nên thử lại các lỗi tạm thời như thế nào?

Chỉ thử lại `concurrency_limit`, `fetch_timeout` và, một hoặc hai lần, `fetch_failed`. Chờ trước mỗi lần thử mới và nhân đôi thời gian chờ mỗi lần, kèm một chút jitter ngẫu nhiên để các worker song song không thử lại cùng lúc. Dừng sau 4 hoặc 5 lần thử.

Với `concurrency_limit`, cách khắc phục tốt hơn là gửi ít yêu cầu hơn cùng một lúc: Mỗi tài khoản có giới hạn số trang đọc cùng lúc; dashboard của bạn hiển thị giới hạn đó, và bộ phận hỗ trợ có thể tăng lên. Với `fetch_timeout`, hãy tăng `timeout` lên tới 60 giây trước khi thử lại một trang web chậm. Thử lại không tốn gì, vì lần thử thất bại không bao giờ bị tính phí.

*Python: chỉ thử lại những lỗi có thể thay đổi*

```python
import os
import random
import time

import requests

API = "https://webreaderapi.com/api/v1/read"
HEADERS = {"Authorization": "Bearer " + os.environ["WEBREADER_API_KEY"]}
RETRY = {"concurrency_limit", "fetch_timeout", "fetch_failed"}
FINAL = {"blocked_by_robots", "blocked_by_site_policy", "target_refused", "unsupported_content"}


def read_page(url, attempts=5, **options):
    wait = 1.0
    for attempt in range(1, attempts + 1):
        resp = requests.get(API, headers=HEADERS, params={"url": url, **options}, timeout=75)
        if resp.status_code == 200:
            return resp.json()
        error = resp.json().get("error", {})
        code = error.get("code", "unknown")
        if code in FINAL:
            return {"url": url, "skipped": code, "target_status": error.get("target_status")}
        if code not in RETRY or attempt == attempts or (code == "fetch_failed" and attempt >= 3):
            raise RuntimeError(f"{code}: {error.get('message')}")
        time.sleep(wait + random.uniform(0, wait / 2))
        wait = min(wait * 2, 30)
    raise RuntimeError("unreachable")


if __name__ == "__main__":
    print(read_page("https://example.com/blog/first-post", max_chars=20000).get("title"))

```

*Node.js: quy tắc tương tự với exponential backoff*

```javascript
const RETRY = new Set(['concurrency_limit', 'fetch_timeout', 'fetch_failed']);
const FINAL = new Set(['blocked_by_robots', 'blocked_by_site_policy', 'target_refused', 'unsupported_content']);
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

export async function readPage(url, options = {}, attempts = 5) {
  let wait = 1000;
  for (let attempt = 1; attempt <= attempts; attempt++) {
    const params = new URLSearchParams({ url, ...options });
    const res = await fetch(`https://webreaderapi.com/api/v1/read?${params}`, {
      headers: { Authorization: `Bearer ${process.env.WEBREADER_API_KEY}` },
    });
    const body = await res.json();
    if (res.ok) return body;
    const code = body.error?.code ?? 'unknown';
    if (FINAL.has(code)) return { url, skipped: code, target_status: body.error.target_status };
    if (!RETRY.has(code) || attempt === attempts) throw new Error(`${code}: ${body.error?.message}`);
    await sleep(wait + Math.random() * (wait / 2));
    wait = Math.min(wait * 2, 30000);
  }
}

```

## Còn lỗi bên trong một batch hoặc một crawl thì sao?

Một job không dừng lại chỉ vì một trang bị lỗi. Trang đó xuất hiện trong `results` dưới dạng `{url, error}` với cùng các mã như trên, và không tốn credit. Một lần crawl gặp `blocked_by_robots` trên một phần của site chỉ đơn giản là đọc phần còn lại của site.

Khi kết thúc job, hãy đếm các mã lỗi. Nhiều `target_refused` trên một site nghĩa là site không chấp nhận trình đọc tự động; nhiều `fetch_timeout` nghĩa là site chậm, và đặt `timeout` dài hơn trong tùy chọn job có thể giúp ích.

## Lỗi: câu hỏi thường gặp

### Tôi có bị tính phí cho một trang bị robots.txt chặn không?

Không. 1 credit = đọc 1 trang không cần trình duyệt; 2 credit khi trang cần trình duyệt để render JavaScript. Yêu cầu thất bại, hoặc bị robots.txt của trang web từ chối, không bị tính phí. Điều tương tự cũng đúng với mọi lỗi khác trên trang này.

### Các bạn có thể đọc một trang chặn crawler của mình nếu tôi yêu cầu không?

Không. Chúng tôi tôn trọng lựa chọn của chủ site, và chính sách này giống nhau với mọi khách hàng. Nếu bạn sở hữu site, hãy thêm quy tắc cho `WebReaderAPI` trong robots.txt của bạn; [trang crawler](https://webreaderapi.com/vi/bot/) hướng dẫn cách làm.

### Tại sao tôi nhận fetch_failed cho một trang vẫn tồn tại?

Có thể site đã bị down, chậm chấp nhận kết nối TLS hoặc trả về 404 cho client tự động. Hãy thử lại một hai lần với backoff. Nếu vẫn lỗi, hãy kiểm tra địa chỉ trên trình duyệt.

### Sau khi bị từ chối, agent của tôi nên nói gì với người dùng?

Một câu đơn giản, chẳng hạn site không cho phép đọc tự động trang đó. Sau đó để model dùng các nguồn khác. Trả về mã lỗi làm kết quả của tool là đủ với hầu hết model.

---

WebReaderAPI: WebReaderAPI là web reader API cho AI agent, chuyển mọi trang web công khai thành Markdown sạch, thanh toán bằng crypto không cần tải lên giấy tờ. Credit trả trước từ 0,07 US$/1K trang không bao giờ hết hạn, và robots.txt được tôn trọng. Không cần KYC — không ID, không selfie, không giấy tờ. Thanh toán bằng crypto.
