Integrations
URLpipe as a LlamaIndex reader
Rendered pages as LlamaIndex Documents — a reader you own, in about twenty lines.
By Roger Campos · Last updated: September 2026
TL;DR
A LlamaIndex reader for URLpipe is a BaseReader subclass whose load_data posts each URL to https://urlpipe.dev/markdown and returns Documents with the Markdown as text and the URL in metadata. It is a recipe to paste into your project, not an official package, and plugs into VectorStoreIndex.from_documents.
Free plan, no credit card. 1,000 credits a month.
The idea
Why a custom reader
An index is only as good as the text it is built from. URLpipe renders each page in real Chrome, then walks the result to Markdown with navigation and chrome removed — no model involved, so the text is the same every run. Markdown keeps the headings, which LlamaIndex's MarkdownNodeParser turns into nodes that follow the page's own sections.
A recipe, not a package
There is no official URLpipe reader on LlamaHub. The class below is a starting point you own; it depends only on llama-index-core and requests.
Code
The reader
import os
import requests
from llama_index.core import Document
from llama_index.core.readers.base import BaseReader
class URLpipeReader(BaseReader):
"""Read web pages as Markdown through URLpipe's /markdown endpoint."""
def __init__(self, api_key: str | None = None, max_age: str | int = "7 days"):
self.max_age = max_age
self.headers = {"Authorization": f"Bearer {api_key or os.environ['URLPIPE_API_KEY']}"}
def load_data(self, urls: list[str]) -> list[Document]:
documents = []
for url in urls:
res = requests.post("https://urlpipe.dev/markdown", headers=self.headers, timeout=75,
json={"url": url, "sync": True, "max_age": self.max_age})
res.raise_for_status()
documents.append(Document(text=res.text, metadata={"source": url}))
return documentsCode
Building an index
from llama_index.core import VectorStoreIndex
from llama_index.core.node_parser import MarkdownNodeParser
documents = URLpipeReader().load_data([
"https://docs.example.com/getting-started",
"https://docs.example.com/api/authentication",
])
index = VectorStoreIndex.from_documents(
documents,
transformations=[MarkdownNodeParser()],
)
print(index.as_query_engine().query("How do I authenticate?"))Each node keeps the source URL from its Document's metadata, so answers can cite the page they came from.
Want the page's title, author and publication date on every node? Post to /scrape with "operations": ["markdown", "meta"] instead, and put operations.meta.result into the Document's metadata — one page visit, 6 credits.
Scale
Things to change for production
- Slow pages. A sync call waits up to 60 seconds, then answers
504with a token and keeps working; collect it fromGET /result/:token. - Parallelism. Run as many requests at once as your plan allows — the
X-Concurrency-Limitheader tells you — and no more, or the extra ones return429. - Thousands of pages. Send them async with
report_toand insert Documents as webhooks arrive; the RAG ingestion page has the handler. - Refreshing an index. Re-reading pages inside
max_agecosts nothing; a lowermax_agebuys fresher text at 1 credit a page.
Endpoints
The endpoints on this page
- Turn any URL into clean Markdown
Paste a link and get the page's main content as tidy Markdown — headings, lists, links and code kept, navigation and cookie banners stripped. The format LLMs and RAG pipelines work best with.
Try it free - Everything about a URL, from one visit
Paste a link and get a screenshot of the page, its title, description and share image, the main content as Markdown and the console errors it threw — all from the same page load.
Try it free
FAQ
Frequently asked questions
Is there a URLpipe reader on LlamaHub?
Why Markdown instead of HTML for LlamaIndex?
Does it read pages that need JavaScript?
What does it cost to build an index of 1,000 pages?
Connect it in five minutes.
Free plan, no card. Confirm your email and your API key is live — you'll be making real requests in minutes.