A RAG ingestion pipeline on clean Markdown

Convert mixed real-world documents to Markdown first, then chunk by headings — the simplest reliable RAG ingestion pattern, shown in Python.

Last updated

RAG quality is set at ingestion: garbage chunks in, garbage answers out. Converting every source document to Markdown FIRST gives you one clean, heading-structured format to chunk — regardless of whether the source was a PDF, a Word file or scraped HTML.

Pipeline sketch

import pathlib

import httpx

TYPES = {
    ".pdf": "application/pdf",
    ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
    ".html": "text/html",
    ".txt": "text/plain",
}

def to_markdown(path: pathlib.Path) -> str:
    resp = httpx.post(
        "https://api.mdkit.online/v1/convert",
        files={"file": (path.name, path.read_bytes(), TYPES[path.suffix])},
        headers={"X-API-Key": "YOUR_KEY"},
        timeout=120,
    )
    resp.raise_for_status()
    return resp.json()["markdown"]

for doc in pathlib.Path("corpus").iterdir():
    markdown = to_markdown(doc)
    chunks = markdown.split("\n## ")  # chunk on second-level headings
    # …embed + index the chunks…

Scaling up

For a large corpus, switch the loop to the async lane: submit every document up front with a webhook_url, and index each one as its callback arrives — no polling loop, and files up to 50 MB.

Try it

Sign in with a magic link for the key this pipeline puts in X-API-Key — the free tier converts a first corpus before you pick a plan.

FAQ

What is the best document format for RAG ingestion?
Markdown. Converting every source document to Markdown first gives you one heading-structured format to chunk, whatever the original was — which means one chunking strategy instead of one per file type.
Should I chunk by token count or by heading?
Chunk by heading first and only split oversized sections by tokens. Heading boundaries are semantic boundaries; a fixed token window will happily cut a table in half.
Why does ingestion quality matter more than the model?
Because retrieval can only return what you stored. A chunk that lost its table structure at ingestion is wrong in every answer that cites it, no matter which model reads it.