ClawEngine.ai

By use case · Node.js

Node.js web scraping API for JavaScript web scraping, crawling and Puppeteer alternatives

The short answer

A Node.js web scraping API is an HTTP endpoint you call from your own JavaScript instead of installing Cheerio, Puppeteer and a proxy layer inside your app. With ClawEngine you POST a URL, and optionally a schema of the fields you want, then read back clean markdown or typed JSON with the page already rendered. Native fetch is enough, so there is no SDK and no Chromium binary in your Docker image. Cheerio is still the right call for small static pages, and Playwright is still the right call when you need to click through a flow you own. The API takes over when content is built in the browser, when selectors keep breaking, or when a crawl has to run unattended. Plans start at $39 a month.

Clean markdown & JSON · JavaScript rendered · robots.txt respected

Last updated July 2026

Live Extraction
GET
try:

Hit Extract to turn this page into clean, LLM-ready data.

robots.txt respected · public data only

Markdown · JSON · structured fields, from one API call. Crawling, rendering and extracting ...

Node is a good place to scrape from, right up to the moment you deploy. Locally, `puppeteer.launch()` feels like nothing. Then the container image passes a gigabyte, the Lambda cold start doubles, Chromium wants shared memory your orchestrator will not give it, and someone spends a Friday pinning `@sparticuz/chromium` to a version that matches the Puppeteer major. None of that work produced a single field of data. It just kept the browser alive.

ClawEngine moves that half of the job to a server. From Node you send one POST with the target URL, pick markdown or JSON, and pass a schema if you want typed output. Rendering runs remotely, so a Next.js or Vue page that returns a hollow `<div id="root">` to a plain fetch comes back with its content in place. Crawling is the same request with a seed URL and scope patterns instead of one address, which means an entire docs site or category tree arrives without you writing a frontier queue, a visited set, a politeness delay and a retry wrapper on top of `undici`.

Here is the part most vendors skip. Cheerio is excellent and you should keep using it. If the markup is server rendered, the selectors are stable, and the volume is a few hundred pages, `fetch` plus `cheerio.load()` is faster than any network round trip to a third party and costs nothing. Playwright and Puppeteer are also genuinely better than an API for anything interactive: driving a form on your own staging site, screenshotting a dashboard behind your own session, or running end to end tests. Those are control problems, and control is what a local browser gives you.

The trade flips when the failure modes become operational rather than logical. Client-side rendering you cannot see with `curl`. Twenty different source layouts that each need their own selector file. A nightly job that has to survive a marketing redesign without paging anyone. A serverless function where a 900 MB browser bundle is simply not an option. At that point the useful question is not whether Cheerio could do it, since it usually could with enough glue, but whether your team wants to own the glue. Teams building RAG pipelines, price trackers, lead enrichment and agent tools generally do not.

CRAWL RENDER JS EXTRACT MARKDOWN JSON

Any URL in LLM-ready data out

robots.txt respected public data only

Why it works

What you get with Node.js

No Chromium in your image

Native fetch and an API key replace Puppeteer, the browser binary and the memory it needs. Docker images stay small and serverless functions stay under their limits.

Rendered before you see it

React, Vue and Svelte pages that hand a plain fetch an empty root element come back complete, because rendering finishes server side before extraction starts.

Schemas instead of selectors

Describe the fields you want and get a typed object back. A source redesign stops being a pull request against a file full of CSS selectors.

What it handles

Any URL in, clean structured data out

Point ClawEngine at a public page and it crawls, renders the JavaScript and extracts clean markdown or typed JSON in one call. Define a schema for structured fields, and respect robots.txt and Terms of Service by default.

  • Works with native fetch, no SDK to install
  • Returns clean markdown or typed JSON
  • Renders React, Vue and Svelte pages remotely
  • Crawls a whole site from one seed URL
  • Runs fine in Lambda, Workers and small containers
  • Feeds LangChain.js, vector stores and queues directly
POST /v1/extract extraction result
200 · JSON
{
  "url": "https://example.com/products/atlas",
  "title": "Atlas Field Notebook",
  "markdown": "# Atlas Field Notebook\n\nDurable...",
  "data": {
    "name": "Atlas Field Notebook",
    "price": 24.00,
    "currency": "USD",
    "rating": 4.7
  },
  "links": [ "/products", "/cart" ],
  "metadata": { "rendered": true }
}
JS rendered · boilerplate stripped ✓ robots.txt respected

Why ClawEngine

One API that crawls, renders and extracts

Not a raw HTML dump, not a headless browser fleet to run, and not a brittle parser to maintain. One call crawls a public page, renders its JavaScript and returns clean markdown or typed JSON, built for RAG pipelines and AI agents.

LLM-ready output

Clean markdown or typed JSON with the boilerplate stripped, so the data drops straight into a vector store, a prompt or an agent without a cleanup step.

JavaScript rendered

Each page loads in a real browser environment before extraction, so single-page apps and client-rendered content come back complete, not as an empty shell.

Compliance-first

ClawEngine works on public, permitted data only. It respects robots.txt and site Terms of Service and honors crawl-delay, so responsible scraping is the default.

Code examples

Scrape a page from Node in one call

No SDK, no browser binary. Send a URL and the fields you want with native fetch and read a typed object back. Swap the endpoint for /crawl to pull a whole site.

javascript Typed extract with native fetch
const BASE = "https://api.clawengine.ai/v1";
const headers = {
  Authorization: `Bearer ${process.env.CLAWENGINE_API_KEY}`,
  "Content-Type": "application/json",
};

const res = await fetch(`${BASE}/extract`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    url: "https://example.com/products/widget-pro",
    format: "json",
    render: true,                 // content is built client-side
    schema: {
      product_name: "string",
      price: "number",
      currency: "string",
      in_stock: "boolean",
    },
  }),
});

if (!res.ok) throw new Error(`extract failed: ${res.status}`);

const { data } = await res.json();
console.log(data.product_name, data.price, data.currency);
javascript Crawl a docs site to disk for a vector store
import { mkdir, writeFile } from "node:fs/promises";

const BASE = "https://api.clawengine.ai/v1";

const job = await fetch(`${BASE}/crawl`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.CLAWENGINE_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    url: "https://example.com/docs",
    include: ["/docs/*"],         // stay inside the docs tree
    max_pages: 400,
    format: "markdown",
  }),
}).then((r) => r.json());

await mkdir("./corpus", { recursive: true });

for (const page of job.pages) {
  const name = new URL(page.url).pathname.replaceAll("/", "_") + ".md";
  // markdown arrives without nav, banners or script tags, so chunk it directly
  await writeFile(`./corpus/${name}`, `# source: ${page.url}\n\n${page.markdown}`);
}

console.log(`wrote ${job.pages.length} pages`);
javascript Concurrent fan-out with a small pool
const BASE = "https://api.clawengine.ai/v1";
const headers = {
  Authorization: `Bearer ${process.env.CLAWENGINE_API_KEY}`,
  "Content-Type": "application/json",
};

async function extract(url) {
  const res = await fetch(`${BASE}/extract`, {
    method: "POST",
    headers,
    body: JSON.stringify({ url, format: "json", render: true,
      schema: { title: "string", published_at: "string", author: "string" } }),
    signal: AbortSignal.timeout(90_000),
  });
  if (!res.ok) return { url, error: res.status };
  return { url, ...(await res.json()).data };
}

// hand-rolled pool: keep your own concurrency sane, no p-limit needed
async function pool(urls, size = 8) {
  const queue = [...urls];
  const out = [];
  const worker = async () => {
    for (let next = queue.shift(); next; next = queue.shift()) {
      out.push(await extract(next));
    }
  };
  await Promise.all(Array.from({ length: size }, worker));
  return out;
}

const rows = await pool(["https://example.com/a", "https://example.com/b"]);
console.table(rows);

People also ask

Node.js web scraping API: the questions buyers ask

How do I scrape a website in Node js?

Fetch the page and parse it, or call an API that does both. The classic route is `fetch()` for the HTML and Cheerio for the selectors, which works whenever the server sends real markup. If the content appears only after JavaScript runs, you either launch Puppeteer or Playwright locally, or POST the URL to a scraping API and read back rendered markdown or typed JSON.

Is Puppeteer or Cheerio better for web scraping?

Cheerio is better whenever the HTML already contains your data, because it is a parser with no browser attached and it runs in milliseconds. Puppeteer is better when the page builds itself in the browser or you need to click, scroll and wait. The cost difference is large: Cheerio adds a small dependency, Puppeteer adds Chromium plus the operational care it needs in production.

Can Node js scrape JavaScript rendered pages?

Yes, but not with fetch alone, since fetch returns the same empty shell the server sent. Your options are running a headless browser through Puppeteer or Playwright, or sending the URL to a service that renders it and returns the finished DOM as markdown or JSON. The first keeps everything in your process and adds a browser to maintain. The second keeps your deployment small.

What is the best Node js web scraping library?

There is no single best one, only a best fit. Cheerio for parsing static HTML, Playwright for interactive or client-rendered pages, Crawlee when you want a full crawling framework with queues and session handling, `undici` when you just need fast HTTP. An API replaces the combination rather than any one of them, and it is worth it mainly when maintenance, not capability, is your bottleneck.

How do I avoid getting blocked when scraping?

Scrape like a well behaved client instead of trying to look like a different one. Read robots.txt and follow it, send a User-Agent that identifies who you are, cap concurrency, respect crawl-delay, back off on 429 and 503 rather than retrying instantly, and cache so repeat runs do not refetch unchanged pages. If a site still refuses you, treat that as an answer and find a licensed source.

How much does a Node js web scraping API cost?

Pricing is usually per request or per credit, with rendered pages costing more than plain fetches. ClawEngine is $39 a month on Hobby, $99 on Startup and $399 on Scale, with Enterprise quoted, and there is no free plan. Compare it against the hours you spend upgrading Chromium images and repairing selectors, not against the zero on the Cheerio npm page.

Good questions

Questions about Node.js

Fetch is enough, and that is intentional. The API is ordinary JSON over HTTP, so anything that speaks HTTP works: native fetch on Node 18 and later, axios, ky, `undici` or got. That also means no dependency to audit, no types to keep in sync with a release, and nothing that breaks when you move from a container to an edge runtime. Add `p-limit` if you want tidy concurrency, but even that is optional.
Yes, and mixing them is usually the cheapest architecture. Keep Cheerio for the static sources it already handles well, since paying for a network hop there buys you nothing. Keep Playwright for flows you authenticate into yourself or for visual tests. Route only the awkward sources through the API: client-rendered pages, sources that change layout often, and anything that has to run in a function with a tight bundle limit. Migrating one source at a time avoids a rewrite.
Ask for markdown when the destination is retrieval. It arrives without navigation, cookie banners or script tags, so you can split it on heading boundaries and embed each chunk with the source URL and heading path as metadata for LangChain.js or a Postgres vector column. Ask for JSON with a schema when the destination is a database or a queue, since every record shares the same keys and can be validated with Zod before it is written.

Explore more

More ways to turn the web into data with ClawEngine

Stop wrangling raw HTML. Get LLM-ready data.

Point ClawEngine at a public page and one call crawls, renders the JavaScript and extracts clean markdown or typed JSON, ready for your RAG pipeline or AI agent. Public, permitted data only.

See pricing

Crawl · render JS · extract markdown & JSON · robots.txt respected, public data only