Does MarkItDown Do OCR? What Happens When You Feed It a Scanned PDF
Aug 13, 2026 • 8 min read
MarkItDown is the default recommendation for turning documents into markdown for LLMs, and it works beautifully until someone hands you a scan. Here is exactly what the shipped code does with a PDF, what its README means by OCR, and what it costs to fix.
// Try it now, no signup required
PDF, JPG, PNG, BMP, HEIC, TIFF
Upload a document to extract
Drop files here or click to upload
Up to 50 files
Free plan extracts the first 5, rest can be unlocked after
Uploading...
Free on your own files. No credit card, no signup to test.
The short answer: no, MarkItDown does not do OCR on PDFs. Its PDF converter reads the embedded text layer through pdfminer and pdfplumber, both of which extract characters that are already stored in the file. A scanned PDF has no stored characters, only a picture of a page, so MarkItDown returns an empty or near-empty markdown file. Nothing errors. You just get nothing.
This catches a lot of teams, because MarkItDown is genuinely good and it is the first thing most people reach for. Microsoft describes it as a lightweight Python utility for converting various files to markdown for use with LLMs and related text analysis pipelines, and for born-digital documents that is exactly what it is. The trouble starts when a real document pile arrives and roughly a third of it came off a scanner.
What the source actually does
We read the shipped converters on GitHub on 13 August 2026 rather than trusting the feature list, because the feature list is where the confusion comes from.
The PDF converter imports three things: pdfminer, pdfminer.high_level and pdfplumber. That is the whole extraction stack. Both libraries walk the content stream of the PDF and pull out text objects. There is no recognition step, no image preprocessing, and no call to an external service. If the content stream contains a single full-page image and no text objects, there is nothing for either library to return.
The image converter is more interesting, because this is where the README's OCR claim lives. Its class docstring reads: converts images to markdown via extraction of metadata (if exiftool is installed), and description via a multimodal LLM (if an llm_client is configured). In practice it does two things. It reads EXIF fields such as ImageSize, Title, Caption, Description, Keywords, Artist, Author, DateTimeOriginal, CreateDate and GPSPosition, and writes them out as lines. Then, only if you have supplied both an LLM client and a model name, it asks that model to describe the picture and appends the result under a Description heading.
It also accepts a narrow set of inputs: JPEG and PNG, by mime type or by the .jpg, .jpeg and .png extensions.
So is an LLM description the same as OCR?
Not quite, and the difference matters more than it sounds. A multimodal model looking at a photograph of an invoice can and often will read the text back to you, so if you configure an LLM client you are not getting nothing. But you are getting a generated description of an image, not a transcription with a defined contract. Ask it twice and you can get two different answers. Ask it for a table and you may get a summary of the table. There is no bounding box telling you where a value came from and no confidence score telling you the model was unsure, which is the thing you would gate a review queue on.
There is also a cost that people miss when they call this free. The LLM description path bills against whatever model you configured, at that model's token rates. It is not part of MarkItDown.
And none of this helps with the case that actually hurts, because the PDF path has no LLM branch at all. Configure the best multimodal model in the world and a scanned PDF still goes through pdfminer and pdfplumber and still comes back empty. If you want the LLM path for a scan, you have to rasterize the pages to images yourself first, at which point you are writing the pipeline rather than using the tool.
How to tell whether your PDF has a text layer
Open it in any viewer and try to select a sentence with your cursor. If the text highlights word by word, there is a text layer and MarkItDown will handle it. If your cursor draws a rectangle over the page like it would over a photo, the page is an image.
At scale you want this as a check rather than a habit. Extract the text from the first two pages, count the characters, and treat anything under a low threshold as a scan. Split the pile at ingest and route each half differently. This is worth doing for its own sake: running clean digital PDFs through a paid recognition API is the most common way teams overpay, and it is invisible on the invoice because the pages look identical.
What to use when the file comes back empty
Once you accept that scans need recognition, the question becomes which service and on which meter, and the answer is less obvious than it looks. The three cheapest recognition meters in the industry, Azure Read, AWS Textract and Google Enterprise Document OCR, all cost $1.50 per 1,000 pages, and none of the three returns markdown. Getting markdown out of Azure means the Layout model at $10.00 per 1,000 pages. Getting it out of AWS means Amazon Bedrock Data Automation at $10.00 per 1,000 pages, because Textract returns Block objects and has no markdown output in any operation.
The markdown-native options are cheaper for this specific job. Mistral OCR 4 returns markdown as the main field of every page at $4.00 per 1,000 pages, or $2.00 in batch, and it hands back confidence scores and block bounding boxes in the same response. LlamaParse, Reducto and Landing AI ADE all produce markdown at their own credit rates. We put every one of them side by side, with the exact parameter each service uses, on the PDF to markdown API comparison.
Frequently asked questions
Does MarkItDown work on scanned PDFs?
No. MarkItDown converts PDFs with pdfminer and pdfplumber, which read the text already embedded in the file. A scanned PDF contains an image of the page and no embedded text, so the conversion produces an empty or near-empty markdown file rather than an error. You need an OCR service to read a scan.
Why does the MarkItDown README mention OCR?
The supported-formats list includes images with EXIF metadata and OCR. Reading the shipped image converter, what it does is extract EXIF metadata and, if you configure a multimodal LLM client, append that model's description of the picture. That description can include text the model happens to read, but it is a generated description rather than a recognition step, and it does not apply to the PDF path at all.
Can I add OCR to MarkItDown myself?
You can, and plenty of teams do: rasterize each PDF page to a PNG, run a recognition engine over it, and stitch the results back into markdown. What you are building at that point is reading order, table reconstruction and error handling, which is most of the work a document API does for you. Whether that is worth it comes down to volume, and we worked the arithmetic through in what self-hosted OCR actually costs.
Is MarkItDown better than pymupdf4llm?
They fail on the same input, so the comparison rarely decides anything important. Both read the text layer, both are fast and free, and both return nothing on a scan. Pick on output shape and file-type coverage for the documents you actually have, then plan separately for the scanned portion of your pile.
How much does it cost to OCR the scanned half of a document pile?
Between $1.50 and $30.00 per 1,000 pages depending on the meter, which is a twentyfold spread for what buyers think of as the same task. Plain text recognition sits at the bottom, markdown-capable meters run from $2.00 to $10.00, and agentic parsing sits at the top. The full normalized table is on OCR pricing per 1,000 pages.
The wider point
Markdown became the default interchange format for LLM pipelines because it is close to plain text, so it is token-efficient, and because headings, lists and tables survive as structure the model already understands. That is why so many tools now emit it, whether the source is a PDF, a spreadsheet or a page you pulled off the web with a scraping API that returns clean, LLM-ready data. The format is settled.
What is not settled is where the text comes from in the first place. A converter can only reformat characters that exist. When they do not exist, no amount of markdown tooling helps, and the honest fix is a recognition step you pay for per page. If you are calling that step from Python, we keep working examples on the Python OCR API reference.
MarkItDown behavior described here was verified against the project source on GitHub on 13 August 2026. Library implementations change, so check the converter for the version you have pinned before relying on any of it.
Extract your documents with DocuOCR
DocuOCR's AI OCR software turns any document into clean, structured data in seconds. No template setup required.
Start free