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_KEY for 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 ocr on auto, or force it with ocr=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.