# Web scraping with JavaScript and Node.js

> Use Node.js fetch to extract public webpages as Markdown or JSON fields. Includes a runnable request, timeout handling, and response-quality checks.

By FetchRelay. Updated 2026-09-06.
Canonical: https://fetchrelay.com/guides/web-scraping-javascript

## What you will build

A server-side Node.js script that turns one URL into Markdown with the built-in fetch API. You can use the same request in a backend job or automation. Keep it on the server: shipping an API key inside frontend JavaScript gives visitors access to your account allowance.

## Before you start

Use Node.js 22 or later. Create a key in [FetchRelay Account](https://fetchrelay.com/account) and set FETCHRELAY_API_KEY in your environment. No FetchRelay SDK or browser package is required. Successful extraction consumes one credit, including when the request returns cached output.

## Request and inspect a page

Save the example as scrape.mjs and run node scrape.mjs. The request asks for two formats from the same page, not two separate page requests.

```javascript
const key = process.env.FETCHRELAY_API_KEY;
if (!key) throw new Error('Set FETCHRELAY_API_KEY first.');

const response = await fetch('https://fetchrelay.com/api/scrape', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer ' + key,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    url: 'https://example.com',
    formats: ['markdown', 'links'],
    mode: 'http',
    onlyMainContent: true,
    timeout: 25000,
  }),
  signal: AbortSignal.timeout(40000),
});
const result = await response.json();
if (!response.ok || !result.success) {
  throw new Error(result.error || 'Extraction failed');
}
for (const warning of result.diagnostics?.warnings || []) {
  console.warn('Review:', warning);
}
console.log(result.data.markdown);
console.log(result.data.links);
```

## Know what a successful result means

The Markdown should include Example Domain. The links array should contain the source page’s public links. Review data.metadata.sourceURL to detect an unexpected redirect. If diagnostics.warnings is present, make an explicit decision about whether the output is acceptable for your task. A generic corporate homepage is not a successful replacement for a requested product page.

Fetched text is untrusted data. Render it as escaped text or through a Markdown renderer that does not execute raw HTML. Never evaluate extracted scripts. If you feed content to an agent, do not let a webpage override the agent’s instructions or access secrets.

## Choose the format for the next consumer

Use markdown when headings and links are useful to readers or retrieval systems. Use text for plain comparisons. Use links for URL inventories. For page-specific records, add named CSS selectors through extract and validate data.extract. Use [the workbench](https://fetchrelay.com/) to inspect a selector before putting it in a recurring script.

## Move beyond one URL

A saved [crawl or batch job](https://fetchrelay.com/use-cases/bulk-web-scraping) is more suitable than an unbounded Promise.all loop. It provides a job ID, progress, partial results, and exports. Start with a small page limit and poll the existing job until completion. Remember that available credits and capacity limits are different constraints.

## Handle failure deliberately

Keep a client timeout longer than the requested extraction timeout. A client-side timeout does not prove the server stopped, so avoid blind immediate retries. Respect rate-limit responses. Switch to Browser for a page that needs JavaScript, not as a promise that every blocked site will work. Read the [rendering guide](https://fetchrelay.com/guides/http-vs-browser-scraping) and [API reference](https://fetchrelay.com/api/openapi) before scaling.

Reference: [Node.js fetch documentation](https://nodejs.org/api/globals.html#fetch).
