RAG Web Crawler — Web Pages to Markdown & Text Chunks
Crawl public web pages into clean Markdown, plain text, and overlapping text chunks with source URLs and character offsets, ready to embed for RAG.
key features
Clean Markdown and plain text from each page, with headings, lists, links, code blocks, and basic tables
Ready-to-embed chunks that overlap and end at paragraph or sentence breaks, each with a stable id, its source URL and title, and exact character offsets for citations
Main content first: menus, headers, footers, and sidebars are left out, and CSS selectors remove anything else you name
Link following within each site, up to five links deep, with every page fetched once
Search instead of URLs: each result page of a query is crawled like a start URL (needs a paid Apify plan)
robots.txt respected by default, with polite pacing between requests to the same site
Pages kept in their own language — English, Chinese, and others — without translation
A status and an error code for every page, and a summary for every run with counts and a stop reason
use cases
- Building a RAG chatbot's knowledge base from documentation and help-center pages
- Grounding AI agents in current web pages they can cite
- Turning a list of articles into embeddings for semantic search
- Collecting the pages a search finds on a topic, in their original language
- Refreshing a vector store on a schedule, with chunk ids that stay the same while a page does not change
- Giving AI agents web pages as Markdown through Apify's MCP server
input parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
startUrls | array | optional | Public page URLs to crawl, up to 1,000 |
query | string | optional | A search query whose result pages are crawled (needs a paid Apify plan) |
queries | array | optional | More search queries, merged with query |
maxResultsPerQuery | number | optional | Search results to crawl per query (default 5, at most 50) |
maxPages | number | optional | Most distinct pages a run schedules (default 20, at most 1,000) |
maxDepth | number | optional | How many links deep to follow within each site (default 0 = start URLs only, at most 5) |
maxConcurrency | number | optional | Pages processed at the same time (default 3) |
maxSearchRequests | number | optional | Search requests per run, retries included (default 10, 0 = no search) |
maxFetchRequests | number | optional | All network attempts per run, robots.txt and retries included (default 100) |
requestTimeoutSecs | number | optional | Timeout for each request, in seconds (default 30) |
maxResponseBytes | number | optional | Largest page response accepted (default 2 MiB) |
maxContentChars | number | optional | Longest text kept per page, in characters (default 50,000) |
maxOutputBytes | number | optional | Total output per run (default 20 MiB) |
respectRobotsTxt | boolean | optional | Check each site's robots.txt first (default on) |
onlyMainContent | boolean | optional | Keep only the page's main content (default on) |
removeSelectors | array | optional | CSS selectors of elements to remove before extraction |
chunkSize | number | optional | Chunk length in characters, not tokens (default 1,000) |
chunkOverlap | number | optional | Characters shared by consecutive chunks (default 150) |
Output Example
One saved page from a real run, with the text shortened and one of its five chunks shown:
1{
2 "schemaVersion": "1.0",
3 "url": "https://www.w3.org/WAI/tutorials/tables/one-header/",
4 "title": "Tables with One Header",
5 "description": "Accessibility resources free online from the international standards organization: W3C Web Accessibility Initiative (WAI).",
6 "languageCode": "en",
7 "canonicalUrl": "https://www.w3.org/WAI/tutorials/tables/one-header/",
8 "markdown": "# Tables with One Header\n\nin [Tables Tutorial](https://www.w3.org/WAI/tutorials/tables/)\n\n## Overview\n\n…",
9 "text": "Tables with One Header\n\nin Tables Tutorial\n\nOverview\n\nThis page covers tables that have one simple header for rows or one simple header for columns. …",
10 "chunks": [
11 {
12 "id": "495cb4e58e13bbf9b5cdbe922f7ea652973ca4dfda61b952aaa021db045a450c",
13 "index": 0,
14 "text": "Tables with One Header\n\nin Tables Tutorial\n\nOverview\n\nThis page covers tables that have one simple header for rows or one simple header for columns. …",
15 "startChar": 0,
16 "endChar": 988,
17 "sourceUrl": "https://www.w3.org/WAI/tutorials/tables/one-header/",
18 "title": "Tables with One Header"
19 }
20 ],
21 "contentTruncated": false,
22 "sources": [],
23 "crawl": {
24 "depth": 0,
25 "httpStatusCode": 200,
26 "loadedAt": "2026-10-19T07:03:53.643Z",
27 "contentType": "text/html",
28 "renderingMode": "http",
29 "extractionMethod": "main"
30 }
31}
Chunk offsets count Unicode code points in text. Each run also writes REQUESTS, an outcome for every page with a status and an error code, and OUTPUT, the run summary with counts, the stop reason, and the events charged.
Tips
- Use
idas the record ID in your vector store. It stays the same across runs while the page and your chunk settings don’t change, so writing chunks byidon a scheduled run replaces them instead of adding copies. - Start with a few pages. Check the Markdown of your target pages before raising
maxPagesormaxDepth, and drop leftovers such as related-link boxes withremoveSelectors. - Read
REQUESTSalongside the Dataset. A run can succeed while some pages were skipped or failed, and each one says why.
faq
How is it different from a general web scraper?
Do I need an API key?
Does it render JavaScript?
Which pages can it read?
Is crawling websites allowed?
related in ~/utility
Universal Web Printer — URL & HTML to PDF, Image
Convert URLs and HTML to PDF, PNG, JPEG, or WebP.
open