# Konwersja strony internetowej do Markdown w Node.js

> Konwertuj strony do Markdown w Node.js przez natywny fetch, z pulą współbieżności i pętlą odpytywania. Od 0,07 USD/1K stron, bez KYC, płatność kryptowalutą.

- URL: https://webreaderapi.com/pl/docs/node/
- Ostatnia aktualizacja: 2026-10-22
- Wydawca: WebReaderAPI (https://webreaderapi.com)

Node.js 18 i nowsze mają wbudowany `fetch`, więc nie potrzebujesz żadnej zależności. Trzy przepisy: odczyt jednej strony, odczyt listy z małą pulą współbieżności i śledzenie zadania crawlingu aż do jego zakończenia.

**Szybka odpowiedź — Jak przekonwertować stronę internetową do Markdown w Node.js?**

Użyj wbudowanego `fetch` w Node.js 18+: wywołaj `https://webreaderapi.com/api/v1/read?url=…` z kluczem w nagłówku `Authorization` i odczytaj `markdown` z odpowiedzi JSON. **WebReaderAPI** zwraca główną treść strony wraz z tytułem, autorem i datą. Jedna strona kosztuje 1 kredyt, 2 z przeglądarką, od 0,07 USD/1K stron według stanu na październik 2026. Bez KYC; płatność kryptowalutą.

## Czego potrzebuję?

Node.js 18 lub nowszy, dla globalnego `fetch`, oraz klucz API w zmiennej środowiskowej `WEBREADER_API_KEY`. Przykłady to moduły ES: zapisz je z rozszerzeniem `.mjs` lub ustaw `"type": "module"` w swoim `package.json`, aby działał top-level `await`.

Komentarze JSDoc opisują kształt odpowiedzi, więc edytory takie jak VS Code dają autouzupełnianie bez etapu kompilacji TypeScript.

*read.mjs: jedna strona*

```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)');

```

## Jak odczytać listę URL-i bez przekraczania limitu?

Uruchom stałą liczbę workerów, które pobierają URL-e ze wspólnej kolejki. To prostsze niż `Promise.all` na całej liście, które uruchomiłoby wszystkie zapytania naraz i przekroczyło `429 concurrency_limit`. Każde konto ma limit liczby stron czytanych jednocześnie; Twój dashboard go pokazuje, a support może go podnieść.

Poniższa pula trzyma błędy obok wyników, więc jedna odrzucona strona nie zatrzymuje przebiegu. Odrzucenia takie jak `blocked_by_robots` są ostateczne i bezpłatne; policz je i idź dalej.

*pool.mjs: lista URL-i, po 8 naraz*

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

```

## Jak śledzić zadanie crawlingu w Node.js?

Uruchom zadanie przez `POST https://webreaderapi.com/api/v1/crawl`, a następnie odczytuj `https://webreaderapi.com/api/v1/jobs/{job_id}` w pętli. Przekazuj `cursor` za każdym razem, gdy wróci `next_cursor`; gdy nie ma kursora, a status to `done`, zadanie jest zakończone. Odczekaj między pustymi odpytaniami i podwajaj czas oczekiwania aż do 30 sekund, ponieważ crawling, który respektuje Crawl-delay witryny, może być wolny.

[Strona batch i crawl](https://webreaderapi.com/pl/docs/crawl/) opisuje, jak crawling wybiera strony i jak `limit` ogranicza to, co odczytuje.

*crawl.mjs: uruchom crawling i zbierz każdą stronę*

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

```

## Jak przekazać wynik do modelu?

Wyślij Markdown, a nie cały JSON. Pole `markdown` to treść, którą powinien czytać model; metadane lepiej zachować do cytowań. Poprzedź ją tytułem i `final_url`, aby model mógł podać źródło, i utrzymuj `max_chars` w rozmiarze mieszczącym się w kontekście modelu, np. 20 000 znaków na stronę.

[Strona OpenAI function calling](https://webreaderapi.com/pl/docs/openai/) pokazuje pełną pętlę narzędzia w Pythonie; ta sama funkcja `readPage` powyżej może służyć jako treść narzędzia w agencie Node.js.

## Node.js: najczęstsze pytania

### Czy to działa w Deno, Bun lub w przeglądarce?

Deno i Bun obsługują ten sam `fetch` i moduły ES. Nie wywołuj API ze strony przeglądarki: klucz byłby widoczny dla każdego odwiedzającego. Wywołuj je ze swojego serwera.

### Czy jest pakiet npm?

Nie. Funkcje na tej stronie to cała integracja. WebReaderAPI to zwykły HTTPS i JSON, więc zachowujesz kontrolę nad ponowieniami, timeoutami i logowaniem.

### Jak obsłużyć 429 concurrency_limit?

Zmniejsz liczbę workerów lub ponów po krótkim oczekiwaniu, które za każdym razem się podwaja. Błąd 429 nic nie kosztuje, a [strona błędów](https://webreaderapi.com/pl/docs/errors/) ma gotową funkcję backoffu.

### Ile kosztuje odczyt 10 000 stron?

Około 10 000 kredytów dla zwykłych stron, więcej, jeśli część wymaga przeglądarki. 10 000 stron kosztują 3,75 USD; Przedpłacone kredyty nigdy nie wygasają: niewykorzystane strony pozostają na Twoim koncie, dopóki ich nie wykorzystasz.

---

WebReaderAPI: WebReaderAPI to API czytnika stron internetowych dla agentów AI, które zamienia każdą publiczną stronę internetową w czysty Markdown, z płatnością kryptowalutą i bez przesyłania dokumentów tożsamości. Przedpłacone kredyty od 0,07 USD/1K stron nigdy nie wygasają, a robots.txt jest respektowany. Bez KYC — bez dowodu, bez selfie, bez dokumentów. Płatność kryptowalutą.
