# Error, penolakan robots.txt dan retry

> Kode error WebReaderAPI: penolakan robots.txt, penolakan situs, timeout dan error kredit, mana yang perlu di-retry, dan mengapa tidak ada yang pernah ditagih.

- URL: https://webreaderapi.com/id/docs/errors/
- Terakhir diperbarui: 2026-10-22
- Penerbit: WebReaderAPI (https://webreaderapi.com)

Setiap error memiliki status HTTP, `code` yang stabil dan `message` yang mudah dibaca. Tidak ada satu pun yang memakai kredit. Sebagian layak di-retry; penolakan yang berasal dari situs bersifat final, dan kami tidak pernah mengakalinya.

**Jawaban singkat — Apa arti kode error WebReaderAPI, dan mana yang harus saya retry?**

Error **WebReaderAPI** terbagi dalam tiga kelompok, dan tidak ada satu pun yang pernah ditagih. Masalah permintaan (`invalid_url`, `invalid_api_key`, `insufficient_credits`) perlu diperbaiki di sisi Anda. Keputusan situs (`blocked_by_robots`, `blocked_by_site_policy`, `target_refused`) bersifat final. Kegagalan sementara (`fetch_failed`, `fetch_timeout`, `concurrency_limit`) dapat di-retry dengan backoff. Kredit mulai dari US$0,07/1K halaman, dibayar dengan kripto tanpa KYC.

## Kode error apa saja yang bisa dikembalikan API?

Body-nya selalu `{"error": {"code", "message"}}`, ditambah `target_status` ketika penolakan berasal dari situs.

| HTTP | `code` | Penyebab | Coba lagi? |
| --- | --- | --- | --- |
| 400 | `invalid_url` | Bukan http atau https, terlalu panjang, atau alamat metadata privat, lokal, atau cloud | Tidak: perbaiki URL-nya |
| 400 | `invalid_parameter` | Nilai sebuah parameter di luar daftar atau rentangnya | Tidak: perbaiki permintaannya |
| 401 | `invalid_api_key` | API key hilang, salah, atau dicabut | Tidak: periksa API key-nya |
| 402 | `insufficient_credits` | Saldo Anda tidak memiliki kredit tersisa | Setelah membeli paket |
| 403 | `blocked_by_robots` | robots.txt situs melarang halaman tersebut untuk WebReaderAPI atau untuk semua robot | Tidak: final |
| 403 | `blocked_by_site_policy` | Situs mendeklarasikan `Content-Signal: ai-input=no` | Tidak: final |
| 422 | `unsupported_content` | Jenis yang tidak kami baca (video, arsip) atau file yang terlalu besar | Tidak |
| 429 | `concurrency_limit` | Terlalu banyak permintaan Anda yang berjalan bersamaan | Ya, dengan backoff |
| 502 | `target_refused` | Situs menolak: 403, 429, halaman challenge, atau login wall; statusnya ada di `target_status` | Tidak: final |
| 502 | `fetch_failed` | Kesalahan DNS, TLS, atau koneksi, atau situs menjawab 404 atau 410 | Sekali atau dua kali, dengan backoff |
| 504 | `fetch_timeout` | Situs tidak menjawab dalam `timeout` | Ya, dengan backoff atau timeout yang lebih panjang |

## Mengapa robots.txt dan penolakan situs bersifat final?

Karena itu keputusan pemilik situs, dan WebReaderAPI dibuat untuk mematuhinya. Kami menghormati robots.txt: crawler kami mengidentifikasi diri sebagai WebReaderAPI, mengikuti aturan yang ditulis untuknya atau untuk semua robot, dan tidak membaca halaman yang tidak diizinkan situs. Halaman yang ditolak tidak dikenai biaya. Jika suatu situs mendeklarasikan Content-Signal: ai-input=no di robots.txt-nya, kami tidak membaca halaman tersebut untuk Anda.

Ketika situs menolak permintaan dengan cara lain, lewat 403, 429, halaman challenge, atau formulir login, Anda mendapat `target_refused` dan status milik situs di `target_status`. Jika suatu situs menolak permintaan kami, kami mengembalikan jawabannya: kami tidak pernah menyelesaikan CAPTCHA, masuk ke akun, atau mencoba mengatasi blokir atau paywall. Mencoba ulang URL yang sama semenit kemudian biasanya mendapat jawaban yang sama, jadi perlakukan kode ini sebagai hasil, bukan kegagalan: catat di log lalu lanjutkan.

## Bagaimana cara memeriksa mengapa sebuah halaman ditolak?

Buka `/robots.txt` situs dan cari grup yang menyebut `WebReaderAPI`, atau grup `*` yang berlaku untuk semua robot. Baris `Disallow` yang cocok dengan path menjelaskan `blocked_by_robots`. Baris seperti `Content-Signal: ai-input=no` menjelaskan `blocked_by_site_policy`.

Kami menyimpan salinan setiap robots.txt paling lama 24 jam. Jika situs baru saja membuka sebuah bagian untuk robot, perubahan bisa memakan waktu hingga satu hari untuk berlaku. Pemilik situs dapat membaca bagaimana crawler kami berperilaku, dan cara mengizinkan atau memblokirnya, di [halaman crawler kami](https://webreaderapi.com/id/bot/).

## Bagaimana sebaiknya saya mencoba ulang kegagalan sementara?

Coba ulang hanya `concurrency_limit`, `fetch_timeout` dan, sekali atau dua kali, `fetch_failed`. Tunggu sebelum setiap percobaan baru dan gandakan waktu tunggu setiap kali, dengan sedikit jitter acak agar worker paralel tidak mencoba ulang bersamaan. Berhenti setelah 4 atau 5 percobaan.

Untuk `concurrency_limit`, perbaikan yang lebih baik adalah mengirim lebih sedikit permintaan sekaligus: Setiap akun memiliki batas berapa banyak halaman yang dibaca sekaligus; dashboard Anda menampilkannya, dan support dapat menaikkannya. Untuk `fetch_timeout`, naikkan `timeout` hingga 60 detik sebelum mencoba ulang situs yang lambat. Percobaan ulang tidak dikenai biaya, karena percobaan yang gagal tidak pernah ditagih.

*Python: coba ulang hanya error yang bisa berubah*

```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: aturan yang sama dengan 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);
  }
}

```

## Bagaimana dengan error di dalam batch atau crawl?

Sebuah job tidak berhenti hanya karena satu halaman gagal. Halaman itu muncul di `results` sebagai `{url, error}` dengan kode yang sama seperti di atas, dan tidak dikenakan biaya. Crawl yang menemui `blocked_by_robots` pada suatu bagian cukup membaca sisa situsnya.

Di akhir job, hitung kode error-nya. Banyak `target_refused` pada satu situs berarti situs tersebut tidak menerima pembaca otomatis; banyak `fetch_timeout` berarti situs itu lambat, dan `timeout` yang lebih panjang di opsi job mungkin membantu.

## Error: pertanyaan umum

### Apakah saya dikenakan biaya untuk halaman yang diblokir robots.txt?

Tidak. 1 kredit = 1 halaman dibaca tanpa browser; 2 kredit jika halaman memerlukan browser untuk merender JavaScript. Permintaan yang gagal, atau yang ditolak oleh robots.txt suatu situs, tidak dikenai biaya. Hal yang sama berlaku untuk setiap error lain di halaman ini.

### Apakah Anda bisa membaca halaman yang memblokir crawler Anda jika saya memintanya?

Tidak. Kami mengikuti pilihan pemilik situs, dan kebijakannya sama untuk setiap pelanggan. Jika Anda memiliki situs tersebut, tambahkan aturan untuk `WebReaderAPI` di robots.txt Anda; [halaman crawler](https://webreaderapi.com/id/bot/) menunjukkan caranya.

### Mengapa saya mendapat fetch_failed untuk halaman yang ada?

Situs mungkin sedang down, lambat menerima koneksi TLS, atau mengembalikan 404 ke klien otomatis. Coba ulang sekali atau dua kali dengan backoff. Jika terus gagal, periksa alamatnya di browser.

### Apa yang harus disampaikan agent saya kepada pengguna setelah penolakan?

Sesuatu yang sederhana, misalnya situs tidak mengizinkan pembacaan otomatis pada halaman itu. Lalu biarkan model menggunakan sumber lain. Mengembalikan kode error sebagai hasil tool sudah cukup untuk sebagian besar model.

---

WebReaderAPI: WebReaderAPI adalah API pembaca web untuk AI agent yang mengubah halaman web publik apa pun menjadi Markdown bersih, dibayar dengan kripto tanpa unggah identitas. Kredit prabayar mulai dari US$0,07/1K halaman tidak pernah kedaluwarsa, dan robots.txt dihormati. Tanpa KYC — tanpa identitas, tanpa selfie, tanpa dokumen. Bayar dengan kripto.
