By use case · LangChain
LangChain web scraping API and LangChain web scraper for scraping any website into clean documents
The short answer
LangChain ships several web loaders, but they split the job: WebBaseLoader fetches HTML without running JavaScript, AsyncChromiumLoader and PlaywrightURLLoader render but need browsers installed on your own machine, and RecursiveUrlLoader crawls without rendering. The pattern that avoids all three limits is a short custom BaseLoader that calls a scraping API and yields LangChain Document objects. ClawEngine renders each public page in a real browser, strips navigation and boilerplate, and returns clean markdown that drops straight into page_content with the canonical URL in metadata for citations. Plans start at $39 a month.
Clean markdown & JSON · JavaScript rendered · robots.txt respected
Last updated July 2026
Hit Extract to turn this page into clean, LLM-ready data.
robots.txt respected · public data only
Nearly every LangChain project that touches the web hits the same wall in the same order. WebBaseLoader works until the first single page app returns an empty shell. Swapping in AsyncChromiumLoader or PlaywrightURLLoader fixes the rendering and hands you a browser dependency to install, patch and scale, which is fine on a laptop and painful in a container on a schedule. RecursiveUrlLoader crawls a site but does not render, and its extractor leaves navigation, cookie banners and footers in the text, which then get chunked and embedded alongside the content you actually wanted.
ClawEngine sits behind a custom loader instead. You write about fifteen lines subclassing BaseLoader, call the API inside lazy_load, and yield Document objects. Rendering, crawl scope, retries and cleaning happen server side, so your repository holds a loader rather than a browser fleet, and the rest of the chain, splitters, embeddings, retriever, never changes. The same endpoint doubles as an agent tool when you want the model to decide which page to read.
The boundary is the same one that applies everywhere on this site. ClawEngine works on public and permitted pages, respects robots.txt and Terms of Service, and does not log into accounts or defeat anti-bot systems. If your chain needs data from behind a login, no loader and no API is the way around that.
Any URL in LLM-ready data out
robots.txt respected public data only
Why it works
What you get with LangChain
A loader, not a browser
Fifteen lines subclassing BaseLoader replace Playwright and its browser binaries in your image, so the container that runs your chain stays slim.
Documents that are already clean
Navigation, cookie banners and footers are stripped server side, so what reaches your splitter is content rather than chrome that competes for retrieval slots.
Same endpoint as an agent tool
Bind the call as a tool and the model chooses which URL to read, receiving markdown it can reason over instead of token-heavy HTML.
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.
- Yields standard LangChain Document objects
- Renders JavaScript with no local browser
- Crawls a whole site from one seed URL
- Keeps the canonical URL in metadata for citations
- Works as a bound tool for LangChain agents
- Respects robots.txt, ToS and crawl-delay
{
"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 }
}
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
A LangChain document loader in fifteen lines
The first sample is the whole integration: subclass BaseLoader, call the API in lazy_load, yield Documents. The second exposes the same call as a tool an agent can invoke on its own.
import os, requests
from typing import Iterator
from langchain_core.documents import Document
from langchain_core.document_loaders import BaseLoader
class ClawEngineLoader(BaseLoader):
"""Crawl a site with ClawEngine and yield LangChain Documents."""
def __init__(self, seed_url: str, max_pages: int = 50):
self.seed_url = seed_url
self.max_pages = max_pages
def lazy_load(self) -> Iterator[Document]:
res = requests.post(
"https://api.clawengine.ai/v1/crawl",
headers={"Authorization": f"Bearer {os.environ['CLAWENGINE_API_KEY']}"},
json={"url": self.seed_url, "format": "markdown",
"render": True, "max_pages": self.max_pages},
timeout=300,
)
res.raise_for_status()
for page in res.json()["pages"]:
yield Document(
page_content=page["markdown"],
metadata={"source": page["url"], "title": page.get("title"),
"crawled_at": page.get("fetched_at")},
)
# Drops into any existing chain unchanged.
docs = ClawEngineLoader("https://docs.example.com").load()
import os, requests
from langchain_core.tools import tool
@tool
def read_web_page(url: str) -> str:
"""Fetch a public web page and return it as clean markdown.
Use this whenever you need the current contents of a URL.
"""
res = requests.post(
"https://api.clawengine.ai/v1/extract",
headers={"Authorization": f"Bearer {os.environ['CLAWENGINE_API_KEY']}"},
json={"url": url, "format": "markdown", "render": True},
timeout=60,
)
res.raise_for_status()
return res.json()["markdown"]
model_with_tools = model.bind_tools([read_web_page])
People also ask
LangChain web scraping API: the questions buyers ask
Can LangChain scrape websites?
Yes. LangChain includes web document loaders, chiefly WebBaseLoader for plain HTML, RecursiveUrlLoader for crawling a URL tree, and SitemapLoader for sites that publish a sitemap. They fetch and parse, so they handle server-rendered pages well. What they do not do on their own is run JavaScript, manage retries and rate limits at scale, or return typed fields, which is why production chains usually put a scraping API behind a custom loader.
How do I scrape a website in LangChain?
For a server-rendered page, WebBaseLoader with a URL is enough. For anything client-rendered or larger than a handful of pages, subclass BaseLoader, call a scraping API inside lazy_load, and yield Document objects with the page text in page_content and the source URL in metadata. Everything downstream, the splitter, the embedding model and the vector store, stays exactly as it was, because a Document is a Document.
What is the best LangChain document loader for JavaScript sites?
Within LangChain itself, AsyncChromiumLoader and PlaywrightURLLoader both render, and both require Playwright and its browser binaries on the machine running the chain. That is fine locally and awkward in a slim container or a scheduled job. A custom loader over a rendering API gets the same rendered content with no browser dependency in your image, which is usually the deciding factor once the pipeline runs anywhere but a laptop.
How do I write a custom document loader in LangChain?
Subclass BaseLoader from langchain_core.document_loaders, implement lazy_load as a generator that yields Document objects, and leave load alone, since the base class already defines it as list(self.lazy_load()). Yielding one Document at a time matters on large crawls, because it keeps the whole corpus out of memory and lets the splitter start work while pages are still arriving.
Should I use LangChain or LlamaIndex for web scraping?
Neither one scrapes better than the other, because both delegate to the same underlying fetch and parse libraries. LlamaIndex has SimpleWebPageReader and a set of web readers that mirror the LangChain loaders almost feature for feature. Pick the framework on the rest of your stack, then feed it clean text from one ingestion source, so switching frameworks later is a change in one file.
Why is my LangChain RAG returning bad answers from scraped pages?
Usually because the retrieved chunks are full of navigation, cookie banners and footers rather than content. Boilerplate survives extraction, gets chunked, gets embedded, and then competes with real text for retrieval slots. Inspect ten random chunks before touching the embedding model. If they contain menu items, the fix is upstream in extraction, not in the retriever.
How do I give a LangChain agent the ability to read a web page?
Wrap the scrape call in a tool with the @tool decorator, give it a clear docstring, and bind it to the model with bind_tools. The agent supplies a URL, the tool returns clean markdown, and the model reads it in the same turn. Return markdown rather than raw HTML, since HTML burns tokens on tags and pushes real content out of the context window.
How much does it cost to scrape the web for a LangChain project?
The framework is free, so the cost is the fetching layer plus embeddings. Scraping vendors charge per request or per credit, and rendered requests cost more than plain fetches everywhere that offers both. ClawEngine is $39 a month for Hobby, $99 for Startup and $399 for Scale, with no free tier. For a one-off load of a few hundred static pages, WebBaseLoader and no vendor at all is the honest answer.
Good questions
Questions about LangChain
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.
Crawl · render JS · extract markdown & JSON · robots.txt respected, public data only