Large language models read Markdown far better than raw PDF text. In this guide you convert a PDF to clean Markdown with the AnyToMarkdown API, get RAG-ready chunks with page numbers, and embed them for semantic search.
What you need
- Python 3.9+ and
pip install requests openai numpy - A RapidAPI key: subscribe to the free plan on AnyToMarkdown
- An
OPENAI_API_KEYfor the embedding step
1. Convert the PDF and get chunks
import requests HOST = "anytomarkdown-pdf-to-markdown-and-document-parser-api.p.rapidapi.com" headers = {"X-RapidAPI-Key": "YOUR_RAPIDAPI_KEY", "X-RapidAPI-Host": HOST} with open("handbook.pdf", "rb") as f: r = requests.post( f"https://{HOST}/v1/convert", headers=headers, files={"file": f}, params={"includeChunks": "true", "includeContent": "false", "chunkSize": 400, "chunkOverlap": 40}, timeout=180, ) r.raise_for_status() chunks = r.json()["chunks"] print(len(chunks), "chunks") includeContent=false skips the full Markdown because you only need the chunks. Each chunk looks like this:
{ "index": 1, "content": "...", "tokens": 392, "headings": ["Employee Handbook", "2. Benefits"], "pageStart": 2, "pageEnd": 2 } Chunks follow the document's sections: they never start in the middle of a table, and each one carries its heading path, so a chunk about vacation days still says it belongs to Benefits.
2. Embed the chunks
from openai import OpenAI import numpy as np client = OpenAI() texts = [c["content"] for c in chunks] response = client.embeddings.create(model="text-embedding-3-small", input=texts) vectors = np.array([item.embedding for item in response.data]) Keep headings and pageStart next to each vector. They let your answers cite a section and page.
3. Search
def search(question, top=3): q = np.array(client.embeddings.create(model="text-embedding-3-small", input=question).data[0].embedding) scores = vectors @ q / (np.linalg.norm(vectors, axis=1) * np.linalg.norm(q)) for i in scores.argsort()[::-1][:top]: c = chunks[i] print(f"{scores[i]:.2f} {' > '.join(c['headings'])} (page {c['pageStart']})") print(c["content"][:200], "\n") search("How many vacation days do employees get?") Tips
- Scanned PDF? Leave
ocronauto, or force it withocr=force. - Only need some pages? Add
pages=1-10. - The free plan accepts 10 MB and 50 pages per request; higher plans raise the limits.
- Want to see the Markdown first? Try the free PDF to Markdown converter.
