2. A Tax Form, a Famous Paper and a Scanned Page, and the Scan Came Back as One Newline
We fed MarkItDown eight real inputs. A scanned PDF returned 1 byte with exit code 0, and the Attention paper came back with 397 words glued together. YouTube, oddly, was flawless.
The WJS Desk
Sep 25, 2026 · 9 min read

In part 1, MarkItDown handled a Word file, a slide deck and a novel without complaint. This part feeds it the files people actually need converted: an IRS tax form, one of the most cited research papers of the decade, a scanned page, a voice memo, a Wikipedia article and a YouTube video. Two of those went well. One came back as a single newline character with exit code 0. One came back with 397 run-together words, which an LLM will read without noticing anything is wrong.
Same setup as part 1: MarkItDown 0.1.8 with the [all] extras, Python 3.12.14, an Apple M4 Pro on macOS 26.5.1. Times are warm runs from /usr/bin/time -p.
| Input | Time | Exit | What came back |
|---|---|---|---|
| IRS Form W-9 (PDF) | 1.00 s | 0 | All the text, no structure, a rotated line spelled backwards |
| "Attention Is All You Need" (PDF) | 1.25 s | 0 | 397 words glued together, tables recovered |
| Scanned W-9 page (PDF, no text layer) | 0.43 s | 0 | One newline. No warning. |
| World Bank spreadsheet (XLS) | 1.01 s | 0 | All the data, plus 458 "NaN" cells |
| Same XLS renamed to .zip | 0.99 s | 0 | Byte-identical to the correct output |
| Spoken memo (WAV) | 4.91 s | 0 | Transcript with two errors, via Google |
| Wikipedia URL | 0.58 s | 1 | HTTP 403 |
| YouTube URL | 13.12 s | 0 | Metadata and full transcript |
The tax form: every word, zero structure
Form W-9 is the form every freelancer in the US fills in, and it is a typical government PDF: boxes, lines, checkboxes and a sidebar printed sideways. Download it and convert:
cd ~/markitdown-test && source .venv/bin/activate
curl -sSL -o docs/w9.pdf https://www.irs.gov/pub/irs-pdf/fw9.pdf
markitdown docs/w9.pdf -o out/w9.md
The 140,815 byte PDF became 38,297 bytes of text across 1,062 lines in 1.00 second. The words are all there and in a sensible order. What is not there is any structure: zero Markdown headings and zero tables. "Part I Taxpayer Identification Number" is just another line of text.
The sideways sidebar is the funny part. The W-9 has "Print or type. See Specific Instructions on page 3" printed vertically along the left edge. MarkItDown turned it into 55 lines of one character each, and because the text runs bottom to top, the letters come out reversed: ., e, p, y, t, and so on down the file. It is harmless for an LLM, which will mostly ignore it, but it is a good reminder that PDF extraction reads positions on a page, not sentences.
The research paper: tables recovered, spaces lost
Next, the paper that introduced the Transformer. It is a single-column arXiv PDF generated by LaTeX, which should be the easy case.
curl -sSL -o docs/attention.pdf https://arxiv.org/pdf/1706.03762v7
markitdown docs/attention.pdf -o out/attention.md
head -20 out/attention.md
Here is the abstract as MarkItDown returned it:
Thedominantsequencetransductionmodelsarebasedoncomplexrecurrentor
convolutionalneuralnetworksthatincludeanencoderandadecoder. Thebest
performing models also connect the encoder and decoder through an attention
mechanism.
Some lines are fine and others have lost every space. To put a number on it we counted whitespace-separated tokens of 25 characters or more, which in normal English prose are almost always glue errors. Then we ran the same PDF through pdftotext from Poppler, which Homebrew installs with brew install poppler, as a baseline.
| MarkItDown 0.1.8 | pdftotext 26.04.0 | |
|---|---|---|
| Words (whitespace separated) | 2,802 | 6,152 |
| Tokens of 25+ characters | 397 | 2 |
| Markdown table rows | 72 | 0 |
| Time | 1.25 s | 0.07 s |
The paper has roughly 6,000 words and MarkItDown's output splits into fewer than half that, because thousands of them are welded together. This is a known bug: issue #120, "Removal of all whitespaces during PDF conversion", opened on 18 December 2024 and still open when we checked. One fix (#1733) was closed without merging, and another (#1902) is open.
The flip side is the tables. MarkItDown recovered the paper's results tables as Markdown pipe tables, and pdftotext did not even try. Table 2, the famous BLEU comparison, came out mostly right, with two errors worth knowing about:
| ConvS2S[9] | 25.16 | 40.46 | 9.6·1018 | 1.5·1020 |
| Transformer(big) | 28.4 | 41.8 | 2.3·1019 | |
First, the superscripts are gone. 1.5·1020 means 1.5 × 1020 training FLOPs, but a model reading it has no way to know that, and "1.5 times 1020" is off by a factor of about 1017. Second, the GNMT+RL row lost its training cost figures. They landed on an unlabelled row directly above it. Every BLEU score we checked in the table matched the paper. The layout is where it went wrong.
Gotcha: none of this raises an error. The command exits 0, and an LLM given glued text will summarise it confidently. If a PDF matters, open the Markdown and read the first screen before you trust anything built on it.
The scanned page: exit 0, one byte
This is the failure that made us rewrite our plans for part 3. We made a scanned document the way a real one gets made: render the W-9's first page to an image, then wrap that image back into a PDF with no text layer. macOS can do both with tools that ship with Poppler and the OS:
pdftoppm -f 1 -l 1 -r 150 -png docs/w9.pdf docs/w9page
sips -s format pdf docs/w9page-1.png --out docs/w9-scanned.pdf
markitdown docs/w9-scanned.pdf > out/scanned.md; echo "exit=$?"
wc -c out/scanned.md
Result: exit=0, and out/scanned.md is 1 byte long. That one byte is a newline. Nothing on standard error. A 653,914 byte PDF of a full page of text went in, and nothing came out, with every signal saying it worked.
Feeding it the PNG directly is the same story: 1 byte, exit 0. After we ran brew install exiftool (6.58 seconds), the PNG returned ImageSize: 1275x1650 and nothing else. MarkItDown can read text out of images, but only by sending them to a vision model through an OpenAI-compatible client, via the markitdown-ocr plugin or its llm_client option, or to Azure Document Intelligence. Every one of those needs a paid key, so we did not test them. Part 4 tries a free, local tool that does OCR scanned pages.
Spreadsheets, and a file that lies about its name
The World Bank's population file has three sheets, and each became a ## section holding a Markdown table. All 265 country and region rows were there. So was a lot of pandas residue:
- 458 cells reading
NaNwhere the spreadsheet had nothing. - 68 column headers reading
Unnamed: 2,Unnamed: 3and so on, because the real header row sits a few rows down the sheet. - Integers turned into floats. The Philippines' 1960 population is
27891897.0.
An LLM will mostly cope, but you are paying tokens for every NaN, and a model asked "which years are missing" has to understand pandas conventions to answer.
The good news is detection. We downloaded that file twice by accident, once saved with a .zip name because we assumed the World Bank served a zip. It was a plain XLS. MarkItDown ignored the wrong extension and produced output byte-identical to the correctly named file. That is Magika, the file type model from part 1, doing its job.
Audio: it works, and your recording goes to Google
We generated a short memo with the Mac's built-in speech synthesiser, in the 16 kHz WAV format transcription tools like:
say -o docs/memo.wav --data-format=LEI16@16000 "Reminder for Thursday. The quarterly report is due at nine thirty, and the budget meeting moved to room four twelve."
markitdown docs/memo.wav
In 4.91 seconds it returned the file's metadata and this:
### Audio Transcript:
reminder for thursday the quarterly report is due at 9:30 and the budget meeting move to room for 12
Two errors in 20 words: "moved" became "move", and "room four twelve" became "room for 12", which is the kind of mistake that sends someone to the wrong room. Our input was synthetic speech with no background noise, so real recordings will likely do worse. We did not test any.
The important part is where the audio went. We read the source. MarkItDown's _transcribe_audio.py calls recognize_google() from the SpeechRecognition library, which uploads the audio to Google's Chromium speech endpoint using a default API key built into that library. No key of yours is involved, and nothing in the command output says your audio left the machine.
Gotcha: do not run MarkItDown on recordings of meetings, calls or anything confidential unless you are fine with Google receiving them. If you only want documents, install without the audio extra, for example 'markitdown[pdf,docx,pptx,xlsx]'.
Web pages: Wikipedia says no, until you change one header
MarkItDown accepts URLs and has a dedicated Wikipedia converter that keeps just the article body. So we tried it:
markitdown https://en.wikipedia.org/wiki/Markdown
That returned requests.exceptions.HTTPError: 403 Client Error: Forbidden in 0.58 seconds. We suspected the User-Agent, so we tested it with curl. With -A "python-requests/2.32.5", which is what MarkItDown sends by default, Wikipedia returned 403. With curl's own User-Agent it returned 200. Wikipedia's policy asks automated clients to identify themselves, and the generic Python one is blocked.
The command line has no option for this, but the Python API lets you pass your own HTTP session. Save this as wiki.py:
import requests
from markitdown import MarkItDown
session = requests.Session()
session.headers.update({"User-Agent": "markitdown-notes/1.0 (personal research script)"})
md = MarkItDown(requests_session=session)
result = md.convert("https://en.wikipedia.org/wiki/Markdown")
print(result.title, len(result.markdown))
open("out/wiki.md", "w").write(result.markdown)
python wiki.py
That fetched and converted the article in 0.28 seconds: 53,481 characters, 17 headings, 51 table rows, with links kept as Markdown links. The infobox and the maintenance banner at the top came through as tables too, so you get some clutter along with the article. For polite use, put a real contact address in the User-Agent string.
YouTube: the pleasant surprise
markitdown "https://www.youtube.com/watch?v=jNQXAC9IVRw"
"Me at the zoo", the first video ever uploaded, came back in 13.12 seconds as 694 bytes: the title, keywords, runtime, the current description and the full spoken transcript, starting "All right, so here we are, in front of the elephants". No key, no account. It uses the youtube-transcript-api package, which reads YouTube's own captions, so it only works on videos that have captions. We ran it once. Caption scrapers like this have a history of being blocked by YouTube, and we did not test what happens after dozens of requests.
What broke, in one list
- Scanned PDFs and images return nothing, with exit code 0 and no warning. This is the dangerous one.
- Words glued together in the arXiv PDF: 397 tokens of 25 or more characters. Known issue #120, open since December 2024.
- Superscripts flattened in tables, so 1020 reads as 1020.
- A table row lost its values to the row above it.
- Wikipedia blocks the default User-Agent with a 403. Fixable only from Python.
- Spreadsheet output carries 458 NaN cells and 68 "Unnamed" headers.
Where MarkItDown was right and we were wrong: the .zip that was really an XLS. We misnamed it, and it quietly corrected us.
Common mistakes
- Treating exit code 0 as success. Check the output size too. Part 3 has a script that does it.
- Assuming a PDF has text because you can read it. If
pdftotext docs/file.pdf - | wc -cprints something tiny, it is a scan. - Running audio through it without realising it goes to Google.
- Fetching lots of pages with the default User-Agent and reading 403s as "the site is down".
- Passing numbers from converted PDF tables to a model without checking the exponents.
What we did not test
We did not test any of the paid paths: Azure Document Intelligence, Azure Content Understanding, LLM image descriptions or the OCR plugin. We did not test Outlook .msg files, Jupyter notebooks, RSS or ZIP archives of mixed files. Our audio was one synthetic clip, not a real recording. We tested one YouTube video and one Wikipedia page. The W-9 and the arXiv paper are two PDFs, and PDFs vary enormously, which is exactly why part 3 runs a dozen of them.
Next
Part 3 is the worked example: turning a folder of IRS publications, 698 pages of dense two-column tax guidance, into Markdown in one batch. It is where we found out that on this kind of document the current release keeps 2% to 6% of sentences intact, and that a release from February keeps most of them.


