# Halaman web ke Markdown di Node.js

> Ubah halaman web menjadi Markdown di Node.js dengan fetch, pool konkurensi kecil, dan loop polling crawl. Dari US$0,07/1K halaman, bayar dengan kripto.

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

Node.js 18 dan versi setelahnya sudah menyertakan `fetch`, jadi Anda tidak perlu dependensi sama sekali. Tiga resep: baca satu halaman, baca daftar dengan pool konkurensi kecil, dan ikuti job crawl sampai selesai.

**Jawaban singkat — Bagaimana cara mengubah halaman web menjadi Markdown di Node.js?**

Gunakan `fetch` bawaan Node.js 18+: panggil `https://webreaderapi.com/api/v1/read?url=…` dengan API key Anda di header `Authorization` dan baca `markdown` dari jawaban JSON. **WebReaderAPI** mengembalikan konten utama halaman beserta judul, penulis, dan tanggalnya. Satu halaman berbiaya 1 kredit, 2 jika memakai browser, mulai US$0,07/1K halaman per Oktober 2026. Tanpa KYC; bayar dengan kripto.

## Apa yang saya butuhkan?

Node.js 18 atau setelahnya, untuk `fetch` global, dan API key di environment variable `WEBREADER_API_KEY`. Contoh-contohnya adalah ES module: simpan dengan ekstensi `.mjs`, atau set `"type": "module"` di `package.json` Anda, agar `await` top-level berfungsi.

Komentar JSDoc menjelaskan bentuk respons, sehingga editor seperti VS Code memberi Anda completion tanpa langkah build TypeScript.

*read.mjs: satu halaman*

```javascript
/**
 * @typedef {Object} Page
 * @property {string} url
 * @property {string} final_url
 * @property {number} status_code
 * @property {boolean} rendered
 * @property {number} credits_used
 * @property {string} [title]
 * @property {string} [author]
 * @property {string} [published_at]
 * @property {string} [language]
 * @property {string} [markdown]
 * @property {number} word_count
 */

/** @returns {Promise<Page>} */
export async function readPage(url, options = {}) {
  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}` },
    signal: AbortSignal.timeout(75_000),
  });
  const body = await res.json();
  if (!res.ok) throw Object.assign(new Error(body.error.message), { code: body.error.code });
  return body;
}

const page = await readPage('https://example.com/docs/getting-started', { max_chars: '20000' });
console.log(page.title, page.word_count, 'words,', page.credits_used, 'credit(s)');

```

## Bagaimana cara membaca daftar URL tanpa terkena rate limit?

Jalankan sejumlah worker tetap yang mengambil URL dari antrean bersama. Ini lebih sederhana daripada `Promise.all` atas seluruh daftar, yang akan memulai setiap request sekaligus dan kena `429 concurrency_limit`. Setiap akun memiliki batas berapa banyak halaman yang dibaca sekaligus; dashboard Anda menampilkannya, dan support dapat menaikkannya.

Pool di bawah menyimpan error di samping hasil, sehingga satu halaman yang ditolak tidak menghentikan proses. Penolakan seperti `blocked_by_robots` bersifat final dan gratis; hitung lalu lanjutkan.

*pool.mjs: daftar URL, 8 sekaligus*

```javascript
import { readPage } from './read.mjs';

export async function readAll(urls, workers = 8) {
  const queue = [...urls];
  const results = [];
  async function worker() {
    while (queue.length) {
      const url = queue.shift();
      try {
        const page = await readPage(url, { max_chars: '50000' });
        results.push({ url, page });
      } catch (err) {
        results.push({ url, error: err.code ?? err.name });
      }
    }
  }
  await Promise.all(Array.from({ length: workers }, worker));
  return results;
}

const urls = Array.from({ length: 30 }, (_, i) => `https://example.com/blog/post-${i + 1}`);
for (const r of await readAll(urls)) console.log(r.url, '->', r.error ?? r.page.title);

```

## Bagaimana cara mengikuti job crawl di Node.js?

Mulai job dengan `POST https://webreaderapi.com/api/v1/crawl`, lalu baca `https://webreaderapi.com/api/v1/jobs/{job_id}` dalam loop. Kirim `cursor` setiap kali `next_cursor` kembali; bila tidak ada cursor dan statusnya `done`, job selesai. Tunggu di antara polling kosong dan gandakan waktu tunggu hingga 30 detik, karena crawl yang menghormati Crawl-delay sebuah situs bisa lambat.

[Halaman batch dan crawl](https://webreaderapi.com/id/docs/crawl/) menjelaskan bagaimana crawl memilih halamannya dan bagaimana `limit` membatasi apa yang dibacanya.

*crawl.mjs: mulai crawl dan kumpulkan setiap halaman*

```javascript
const headers = {
  Authorization: `Bearer ${process.env.WEBREADER_API_KEY}`,
  'Content-Type': 'application/json',
};
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

const start = await fetch('https://webreaderapi.com/api/v1/crawl', {
  method: 'POST',
  headers,
  body: JSON.stringify({ url: 'https://docs.example.com/', limit: 300, include_paths: ['/docs/*'] }),
});
const { job_id } = await start.json();

const pages = [];
let cursor = '';
let wait = 2000;
for (;;) {
  const url = `https://webreaderapi.com/api/v1/jobs/${job_id}${cursor ? `?cursor=${encodeURIComponent(cursor)}` : ''}`;
  const job = await (await fetch(url, { headers })).json();
  pages.push(...job.results.filter((r) => !r.error));
  if (job.next_cursor) {
    cursor = job.next_cursor;
    wait = 2000;
    continue;
  }
  if (job.status === 'done') break;
  await sleep(wait);
  wait = Math.min(wait * 2, 30000);
}
console.log(pages.length, 'pages read, job', job_id);

```

## Bagaimana cara meneruskan hasilnya ke model?

Kirim Markdown-nya, bukan seluruh JSON. Field `markdown` adalah yang sebaiknya dibaca model; metadata lebih baik disimpan untuk sitasi. Beri awalan judul dan `final_url`, agar model dapat menyebutkan sumbernya, dan jaga `max_chars` pada ukuran yang dapat ditampung konteks model Anda, misalnya 20.000 karakter per halaman.

[Halaman OpenAI function calling](https://webreaderapi.com/id/docs/openai/) menunjukkan loop tool lengkap dalam Python; fungsi `readPage` yang sama di atas dapat menjadi isi tool di agen Node.js.

## Node.js: pertanyaan umum

### Apakah ini berfungsi di Deno, Bun, atau browser?

Deno dan Bun mendukung `fetch` dan ES module yang sama. Jangan memanggil API dari halaman browser: API key akan terlihat oleh setiap pengunjung. Panggil dari server Anda sebagai gantinya.

### Apakah ada paket npm?

Tidak. Fungsi-fungsi di halaman ini adalah keseluruhan integrasinya. WebReaderAPI adalah HTTPS dan JSON biasa, sehingga Anda tetap mengendalikan retry, timeout, dan logging.

### Bagaimana cara menangani 429 concurrency_limit?

Kurangi jumlah worker, atau coba lagi setelah jeda singkat yang digandakan setiap kali. 429 tidak berbiaya apa pun, dan [halaman error](https://webreaderapi.com/id/docs/errors/) menyediakan fungsi backoff siap pakai.

### Berapa biaya membaca 10.000 halaman?

Sekitar 10.000 kredit untuk halaman biasa, lebih jika sebagian memerlukan browser. 10.000 halaman berbiaya US$3,75; Kredit prabayar tidak pernah kedaluwarsa: halaman yang belum digunakan tetap ada di akun Anda sampai Anda memakainya.

---

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.
