# Converter página web em Markdown no Node.js

> Converta páginas web em Markdown no Node.js com fetch nativo: uma página, um pool de concorrência e polling. A partir de US$ 0,07/1K páginas, sem KYC.

- URL: https://webreaderapi.com/pt/docs/node/
- Última atualização: 2026-10-22
- Editor: WebReaderAPI (https://webreaderapi.com)

O Node.js 18 e versões posteriores já trazem `fetch`, então você não precisa de nenhuma dependência. Três receitas: ler uma página, ler uma lista com um pequeno pool de concorrência e acompanhar um job de rastreamento até ele terminar.

**Resposta rápida — Como converto página web em Markdown no Node.js?**

Use o `fetch` nativo do Node.js 18+: chame `https://webreaderapi.com/api/v1/read?url=…` com sua chave no header `Authorization` e leia `markdown` da resposta JSON. **WebReaderAPI** retorna o conteúdo principal da página com título, autor e data. Uma página custa 1 crédito, 2 com navegador, a partir de US$ 0,07/1K páginas em outubro de 2026. Sem KYC; pague com cripto.

## Do que eu preciso?

Node.js 18 ou posterior, para o `fetch` global, e uma chave de API na variável de ambiente `WEBREADER_API_KEY`. Os exemplos são ES modules: salve-os com a extensão `.mjs` ou defina `"type": "module"` no seu `package.json`, para que o `await` de nível superior funcione.

Os comentários JSDoc descrevem o formato da resposta, então editores como o VS Code dão autocompletar sem etapa de build do TypeScript.

*read.mjs: uma página*

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

```

## Como leio uma lista de URLs sem estourar o limite?

Rode um número fixo de workers que pegam URLs de uma fila compartilhada. É mais simples do que `Promise.all` sobre a lista inteira, que dispararia todas as requisições de uma vez e estouraria o `429 concurrency_limit`. Cada conta tem um limite de quantas páginas lê ao mesmo tempo; seu dashboard mostra esse limite, e o suporte pode aumentá-lo.

O pool abaixo mantém os erros junto dos resultados, então uma página recusada não interrompe a execução. Recusas como `blocked_by_robots` são definitivas e gratuitas; conte-as e siga em frente.

*pool.mjs: uma lista de URLs, 8 por vez*

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

```

## Como acompanho um job de rastreamento no Node.js?

Inicie o job com `POST https://webreaderapi.com/api/v1/crawl` e depois leia `https://webreaderapi.com/api/v1/jobs/{job_id}` em loop. Passe `cursor` a cada vez que `next_cursor` retornar; quando não houver cursor e o status for `done`, o job terminou. Espere entre os polls vazios e dobre a espera até 30 segundos, já que um rastreamento que respeita o Crawl-delay de um site pode ser lento.

A [página de lote e rastreamento](https://webreaderapi.com/pt/docs/crawl/) descreve como o rastreamento escolhe as páginas e como `limit` limita o que ele lê.

*crawl.mjs: iniciar um rastreamento e coletar todas as páginas*

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

```

## Como passo o resultado para um modelo?

Envie o Markdown, não o JSON inteiro. O campo `markdown` é o que um modelo deve ler; os metadados são melhor guardados para citações. Prefira colocar o título e `final_url` no início, para o modelo poder nomear a fonte, e mantenha `max_chars` em um tamanho que caiba no contexto do seu modelo, como 20.000 caracteres por página.

A [página de function calling da OpenAI](https://webreaderapi.com/pt/docs/openai/) mostra o loop completo de ferramentas em Python; a mesma função `readPage` acima pode servir como corpo da ferramenta em um agente Node.js.

## Node.js: perguntas frequentes

### Isso funciona no Deno, no Bun ou no navegador?

Deno e Bun suportam o mesmo `fetch` e ES modules. Não chame a API a partir de uma página do navegador: a chave ficaria visível para todos os visitantes. Chame a partir do seu servidor.

### Existe um pacote npm?

Não. As funções desta página são toda a integração. WebReaderAPI é HTTPS e JSON puro, então você mantém o controle de retries, timeouts e logging.

### Como trato o 429 concurrency_limit?

Reduza o número de workers ou tente de novo após uma breve espera que dobra a cada vez. Um 429 não custa nada, e a [página de erros](https://webreaderapi.com/pt/docs/errors/) tem uma função de backoff pronta.

### Quanto custa ler 10.000 páginas?

Cerca de 10.000 créditos para páginas simples, mais se algumas precisarem de navegador. 10.000 páginas custam US$ 3,75; Os créditos pré-pagos nunca expiram: as páginas não usadas ficam na sua conta até você usá-las.

---

WebReaderAPI: O WebReaderAPI é uma API de leitor web para agentes de IA que converte qualquer página web pública em Markdown limpo, pago com cripto e sem envio de documentos. Os créditos pré-pagos a partir de US$ 0,07/1K páginas nunca expiram, e o robots.txt é respeitado. Sem KYC — sem documento, sem selfie, sem envio de arquivos. Pague com cripto.
