Sem KYC — sem documento, sem selfie, sem envio de arquivos. Pague com cripto: BTC, USDT, USDC, ETH, SOL, LTC
Idioma: Português
Obtenha uma chave de API

Docs · Node.js

Converter página web em Markdown no Node.js

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.

Última atualização: · 4 min de leitura

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
/**
 * @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
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 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
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 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 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.

A partir de US$ 0,07/1K páginas · sem KYC

Obtenha uma chave de API