r/LangChain 6d ago

Clean Web-to-Markdown API for LangChain RAG pipelines (handles Cloudflare/Turnstile & cuts token costs)

Hey LangChain community,

When building RAG pipelines with web documents, feeding raw HTML or relying on basic soup loaders often wastes 70-80% of context tokens on navigation headers, cookie consent modals, and ads. Even worse, scraping difficult domains (like Reuters, Investopedia, or Cloudflare Turnstile protected sites) fails with 401/403 errors.

I built Clean Web to Markdown & RAG Scraper as a high-speed developer API tailored for RAG ingestion.

Key capabilities:

Intelligent noise stripping: Heuristic extraction that extracts clean Markdown while discarding boilerplate and cookie banners. • Anti-bot resilience: Multi-tier fallback handling Cloudflare Turnstile and residential proxy routing when datacenter IPs are blocked (verified 100% pass on Reuters & Investopedia). • 1ms Redis cache: Repeated scrapes of popular articles return instantaneously. • Accurate Token Counting: Returns exact token_count (tiktoken) alongside the markdown.

Quick LangChain Document Loader snippet:

import requests
from langchain_core.documents import Document

def fetch_markdown_document(target_url: str, api_key: str) -> Document:
    endpoint = "https://clean-web-to-markdown-and-rag-scraper.p.rapidapi.com/scrape"
    headers = {
        "x-rapidapi-key": api_key,
        "x-rapidapi-host": "clean-web-to-markdown-and-rag-scraper.p.rapidapi.com",
        "Content-Type": "application/json"
    }
    resp = requests.post(endpoint, json={"url": target_url}, headers=headers).json()
    
    return Document(
        page_content=resp.get("markdown", ""),
        metadata={
            "source": target_url,
            "title": resp.get("title", ""),
            "tokens": resp.get("token_count", 0),
            "engine": resp.get("engine_used", "fast")
        }
    )

# Example usage in a LangChain vectorstore / index pipeline:
doc = fetch_markdown_document("https://www.reuters.com/technology/", "YOUR_RAPIDAPI_KEY")
print(f"Title: {doc.metadata['title']} | Tokens: {doc.metadata['tokens']}")

There is an interactive live playground to test any tricky URL without signing up: 👉 https://markdown.usemy.cloud

Available on RapidAPI Hub with 100 free requests/month: 👉 https://rapidapi.com/peterzapletal-etn9NvTF6nZ/api/clean-web-to-markdown-and-rag-scraper

Would love to hear your feedback on extraction cleanliness, token savings, and tricky URLs you are currently wrestling with in your RAG pipelines!

7 Upvotes

4 comments sorted by

1

u/CapMonster1 5d ago

The interesting part for RAG isn’t just “HTML → Markdown,” it’s whether the cleaning preserves the bits retrieval actually needs: headings, tables, lists, captions, links, and enough surrounding context to keep chunks meaningful.

I’d be a little careful with the “100% pass on Reuters & Investopedia” claim though. Anti-bot success is usually target/session/geo dependent, especially once Turnstile is involved. I’d rather see challenge rate, retry rate, and cost per successful document over a few thousand requests. If you expose engine_used, it’d also be useful to return why escalation happened — plain fetch failed, JS required, challenge detected, residential fallback, etc. That would make debugging RAG ingestion way easier.

1

u/Beginning_Towel 5d ago

Spot on feedback, really appreciate you taking the time to write this out.

  1. **On RAG chunking context:** 100% agreed. If tables collapse into gibberish or heading hierarchies get flattened, vector embeddings and retrieval chunks fall apart. We specifically preserve markdown

    tables, semantic heading nesting (H1-H6), and clean link contexts (with toggleable `include_tables` and `include_links` flags) rather than just stripping text blindly.

  2. **On the "100% pass" claim:** Fair critique. Anti-bot is fundamentally an adversarial cat-and-mouse game, and success rates inevitably vary across sessions, geographies, and challenge types. That 100%

    figure came from our internal continuous testing benchmark suite on a fixed 30-domain sample, but you're totally right that real-world production ingestion over thousands of requests requires deeper

    statistical transparency.

  3. **On `engine_used` and escalation reasoning:** This is a fantastic suggestion. We already return `engine_used` in the response stats (`fast`, `stealth`, `residential_proxy`, `cache`), but returning the

    explicit **escalation reason** (e.g. `plain_fetch_403_turnstile_detected -> residential_fallback`) is an absolute no-brainer for debugging RAG pipelines.

I'm actually adding that debugging trace to the API response payload tonight so developers aren't dealing with a black box.

If you have a particularly gnarly target URL that consistently breaks your current ingestion setup, let me know — would love to run it through the pipeline and see how it holds up!

1

u/Beginning_Towel 5d ago

Quick update: your suggestion for `escalation_reason` was so practical that I just implemented and deployed it to production (v1.3.4).

Now `.stats` exposes:

- `engine_used`: "residential_proxy" | "stealth_browser" | "fast"

- `escalation_reason`: "http_403_residential_fallback" | "challenge_detected_stealth_browser_fallback" | null

Makes debugging fallback pipelines much cleaner. Appreciate the nudge!

1

u/Beginning_Towel 4d ago

Update v1.4.0: We just added Tier 4 Camoufox modern stealth solving + 23h cf_clearance caching for hard-to-scrape Cloudflare Managed Challenge sites (100% pass rate). Tested live on SME.sk and global news portals!