Web RAG, after the fetch
Context retrieval for RAG over web pages, from fetch to footnote
Context retrieval picks the passages your model answers from. With web pages, quality is set after the fetch: what you keep, how you chunk, rank and cite, and when you re-read the source.
Only Business, Enterprise and Custom sign-ups are reviewed before checkout. Failed fetches cost 0 credits.
import os, datetime, requests
r = requests.post(
"https://livewebdata.com/api/v1/contents",
headers={"Authorization": f"Bearer {os.environ['LIVE_WEB_DATA_API_KEY']}"},
json={"urls": ["https://example.com/docs/setup"],
"text": {"maxCharacters": 100000}}, # the default; raise it for longer pages
timeout=60,
).json()
pages = {p["id"]: p for p in r["results"]} # id = the requested URL
status = r["statuses"][0]
if status["status"] == "success":
page = pages[status["id"]]
record = {
"url": page["url"], # final URL: cite this one
"title": page["title"],
"retrieved_at": status["fetchedAt"], # read live during this request
"truncated": page["textTruncated"],
"markdown": page["markdown"],
}
else:
print(status["error"]["tag"]) # record the failure, do not skip itThe problem
Why RAG over web pages goes wrong after the fetch
None of these are model problems. All of them are decided in the pipeline, usually by what it threw away or never wrote down.
Chunks lose the page they came from
A chunk that says "set the timeout to 30 seconds" is useless without the product, version and section it belongs to. Fixed-size windows cut that context away.
Citations point at pages that changed
The answer cites a URL, but the page was edited last week. The quoted sentence is gone, and the reader who clicks through finds something else.
Removed pages keep answering
A page now returns 404, but its vectors are still in the index. Without a deletion step, retired docs and withdrawn policies keep being retrieved.
Failed fetches vanish without a trace
Timeouts and empty pages drop out of the batch unrecorded, so the index looks complete while parts of the source list are missing.
How it works
How context retrieval for RAG works on web pages
Four steps from a list of URLs to answers with sources. Only the first is about fetching; the other three decide what the model sees.
Step 1: Fetch clean Markdown with metadata
Start from a reviewed list of public URLs. Keep the main content as Markdown plus the final URL, title, published date when present and retrieval time.
Step 2: Chunk by headings, keep the path
Split on headings first, cap long sections, keep tables and code blocks whole, and store the page title and heading path on every chunk.
Step 3: Retrieve, rerank, measure
Combine keyword and vector search, rerank the top candidates and check recall on a small set of real questions before you tune anything else.
Step 4: Cite the passage, refresh the source
Cite the final URL and the section the answer came from. Re-fetch each source on its own schedule, re-embed what changed and delete what is gone.
Use cases
Web pages as RAG context: where the details matter
Five common builds, and the guide that covers the step each one depends on most.
Research over a curated source list
A few hundred pages from named publishers, with a record of what was fetched, when, and which pages failed and why.
Read the guideAssistant over your public docs
Answers from your product documentation, where headings, code blocks and version numbers decide whether retrieval finds the right section.
Read the guideLookups by error code and name
Users paste exact error codes and product names. Hybrid keyword and vector search finds these where embeddings alone can miss them.
Read the guidePolicy and standards Q&A
Answers that a reviewer checks line by line need quote-level citations to the exact page and section, with the date it was read.
Read the guideSupport bot that drops retired pages
When a help article is removed or rewritten, the bot must stop citing the old version at the next refresh, not months later.
Read the guide
Code
Fetch web pages for RAG and keep their metadata
Fetch a URL list, split each page on its headings and attach the final URL, title, heading and fetch time to every chunk. Splitting runs in your code.
import os, requests
from langchain_text_splitters import MarkdownHeaderTextSplitter
urls = ["https://example.com/docs/a", "https://example.com/docs/b"]
r = requests.post("https://livewebdata.com/api/v1/contents", timeout=90,
headers={"Authorization": f"Bearer {os.environ['LIVE_WEB_DATA_API_KEY']}"},
json={"urls": urls,
"text": {"maxCharacters": 100000}}).json()
splitter = MarkdownHeaderTextSplitter([("#", "h1"), ("##", "h2"), ("###", "h3")])
pages = {p["id"]: p for p in r["results"]} # id = the requested URL
chunks, failed = [], []
for st in r["statuses"]: # one status per requested URL
if st["status"] != "success":
failed.append((st["id"], st["error"]["tag"]))
continue
page = pages[st["id"]]
for i, doc in enumerate(splitter.split_text(page["markdown"])):
chunks.append({
"id": f"{page['url']}#{i}", "text": doc.page_content,
"heading_path": list(doc.metadata.values()),
"url": page["url"], "title": page["title"],
"retrieved_at": st["fetchedAt"],
})Markdown is cut at text.maxCharacters, 100,000 characters by default. The splitter is LangChain's; any heading-aware one works. Chunking and storage stay yours.
Approaches
Structure-first vs raw HTML vs crawl once: web RAG compared
Three common ways to build retrieval augmented generation on web sources, compared on what they keep and what they lose over time.
Structure-first pipeline
- What a chunk knows
- Page title, heading path and final URL
- What a citation points to
- Final URL and section, with the date it was read
- Pages that were removed
- Deleted on 404 or 410 at the next refresh
- Freshness signal per chunk
- Fetch time per page (fetchedAt)
- Failed or empty pages
- Logged with an error tag, then retried or replaced
- Effort to set up
- A metadata schema and a refresh job
Raw HTML, fixed windows
- What a chunk knows
- Its own text, menus and banners included
- What a citation points to
- A URL, rarely the right section
- Pages that were removed
- Stay until someone notices
- Freshness signal per chunk
- None
- Failed or empty pages
- Embedded as empty or menu-only chunks
- Effort to set up
- Lowest: one loader and one splitter
Crawl once, never refresh
- What a chunk knows
- Title and URL as of the first crawl
- What a citation points to
- A URL whose content may have changed since
- Pages that were removed
- Keep answering questions
- Freshness signal per chunk
- One crawl date for the whole index
- Failed or empty pages
- Missing, with no record
- Effort to set up
- Low, until the first stale answer
An editorial comparison of pipeline approaches, not of products. Context Retrieval is published by Teneo Labs, which builds Live Web Data API, the fetch step in the code above.
Guides
Five guides to context retrieval, in pipeline order
Each guide covers one step end to end, with starting values to copy, what we have not tested, and the sources behind every claim.
Web content ingestion for RAG: what to keep after the fetch
The source list, metadata schema, document IDs and failure records that decide what your retriever can find and what your model can cite.
Chunking Markdown for RAG: structure first, size second
Split web Markdown on its headings, keep tables and code whole, and give every chunk the page title and heading path it needs to be found.
RAG retrieval for web pages: evaluate, hybrid search, rerank
A 30-question golden set, then contextual chunk headers, hybrid search, reranking and context packing, each tested on your own pages.
RAG citations from web sources: cite the passage, URL and date
Cite the final URL, the passage and the date it was read, verify every citation after generation, and handle pages that change or disappear.
RAG index freshness: re-fetch, re-embed, delete what is gone
Refresh each source on its own schedule, re-embed only what changed, delete what is gone, and know how old every answer's sources are.
FAQ
Context retrieval questions, answered plainly
- What is context retrieval in RAG?
- It is the step that finds the passages a language model answers from: the query is matched against an index of chunks, the best candidates are ranked, and the top few go into the prompt with their sources. The model can only be as right as the passages it is given, so most RAG quality problems are retrieval or ingestion problems.
- Is context retrieval the same as Anthropic's contextual retrieval?
- No. Contextual retrieval is one technique, published by Anthropic in 2024, in which a model writes a short note that places each chunk in its page before indexing. Context retrieval is the whole step of finding the passages a model answers from; the retrieval guide covers the technique and when web pages need it.
- Can RAG use websites as a source?
- Yes. A web page goes through the same pipeline as a PDF: load, clean, chunk, embed, retrieve. The differences are noise (navigation, cookie banners), metadata worth keeping (final URL, title, dates) and change: pages get edited, moved and removed, so a web index needs a refresh and deletion step. Use public pages and respect each site's terms of use and robots.txt. This is not legal advice; check the terms that apply to your use and the product's acceptable use policy.
- How is context retrieval different from a bigger context window?
- A larger window lets you pass more text; it does not choose which text. Passing whole sites is slow and costly, and the "Lost in the Middle" study (Liu et al., 2023) found that models use information in the middle of a long context less reliably than at its edges. Retrieval passes a few relevant passages with their sources, which also makes precise citations possible.
- What should I store with every web page before chunking it?
- At least the final URL after redirects, the page title, the time your pipeline read it, whether the copy was read live or reused from your own store, and a hash of the cleaned content. Add the published date only when the page states one. The ingestion guide has a full schema you can copy.
- Which step of web RAG should I fix first?
- Measure before you change anything: write about 30 real questions, note the page and section that answers each, and check what retrieval returns. If the right chunk does not exist, fix ingestion or chunking. If it exists but ranks low, fix retrieval. If it was retrieved and the answer is still wrong, look at the prompt and the citation check.
- Does Live Web Data API chunk, embed or crawl?
- No. It covers the fetch: you send one or more public URLs per request (see the API reference for the current limit) and get the main content as Markdown, text or HTML, cut at text.maxCharacters (100,000 characters by default), with the final URL, title, fetch time and per-URL errors, which cost 0 credits. Every request reads the page live; there is no cache. Chunking, embeddings, storage, search and crawling stay in your stack. Pages behind logins are out of scope; pages that need JavaScript can be read with the render option. Sign-up is for businesses only.
Notes
New notes on chunking, retrieval and citations
Short, sourced posts on one question each, grouped by pipeline step. The newest appear here first.
· Freshness
Delete stale vectors without deleting pages that only failed once
Removed pages keep answering until their vectors go. Delete on a confirmed 404 or 410, never on a timeout, and watch sync modes that delete what is missing.
· Retrieval
BM25 vs embeddings on documentation pages: which queries each wins
BM25 finds exact strings such as error codes and versions; embeddings find paraphrases. On docs pages you usually need both, fused into one ranking.
· Chunking
Chunk size for RAG: a starting point and one tuning pass
There is no best chunk size for RAG. Start from headings with a cap, count in tokens, and change one setting at a time against real questions.
Start with the fetch, keep the rest yours
Businesses can request API access; only Business, Enterprise and Custom sign-ups are reviewed before checkout. Chunking, retrieval and your index stay in your own stack.