---
title: "HTML to Markdown for LLMs: We Measured the Token Cost on 20 WordPress Pages"
description: "HTML to Markdown for LLMs, measured on 20 WordPress pages: HTML costs 6–12x the tokens on full posts, where that comes from, and which converter to trust."
url: "https://skybootstrap.com/html-to-markdown-for-llm/"
canonical_url: "https://skybootstrap.com/html-to-markdown-for-llm/"
date: "2026-10-28T19:44:00+06:00"
modified: "2026-10-28T13:50:13+06:00"
author: Skybootstrap Team
type: article
site: SkyBootstrap
language: en-US
---

# HTML to Markdown for LLMs: We Measured the Token Cost on 20 WordPress Pages

Giving an LLM the Markdown version of our WordPress articles instead of the HTML page cut the token cost by 83 to 92%. The full HTML page cost 6 to 12 times as many tokens as the same post in Markdown. The surprise is where the saving comes from. Most of it is theme code that has nothing to do with your content, so how you convert matters as much as whether you do.

We measured 20 pages on a WordPress test site, broke the HTML down to see what fills it, and ran five conversion methods on the same posts. If you’d rather serve Markdown from your site than convert pages yourself, the WordPress section near the end covers your options.

## How many tokens does HTML cost compared with Markdown?

On seven full WordPress articles, the HTML page cost 6 to 12 times the tokens of the same post as Markdown. That’s 25,635 to 28,999 tokens against 2,182 to 4,828. That’s 83 to 92% fewer tokens for the Markdown version of each page.

A token is the unit a language model reads and is billed by, often a word or part of one. A tokenizer is the scheme that splits text into tokens; we used o200k_base, the one GPT-4o-class models use.

| Article (about 1,400–3,200 words) | Full HTML | `<main>` only | Markdown | HTML vs Markdown |
| --- | --- | --- | --- | --- |
| Serving Markdown to AI agents | 28,850 | 8,442 | 4,828 | 6.0x |
| llms.txt for WordPress | 28,999 | 8,467 | 4,780 | 6.1x |
| llms.txt examples | 27,437 | 7,009 | 3,758 | 7.3x |
| llms.txt validator guide | 27,318 | 6,883 | 3,497 | 7.8x |
| llms.txt vs llms-full.txt | 26,349 | 6,049 | 2,793 | 9.4x |
| Yoast vs Rank Math vs AIOSEO | 25,993 | 5,354 | 2,270 | 11.5x |
| Cache and Vary: Accept | 25,635 | 5,178 | 2,182 | 11.7x |

Cutting the page down to its `<main>` element, the part that holds the post, already removes most of the cost. It still came to 1.75 to 2.4 times the Markdown. In Twenty Twenty-Five, `<main>` also holds the comment form and a “More posts” list, about 2,400 tokens on our one-paragraph posts. The HTML tags inside the post add the rest.

The ratio depends on how long the post is. Shorter articles get the biggest ratio, because the theme’s overhead stays the same while the content shrinks. Across all 20 pages, including 13 short test posts and pages, HTML totalled 469,549 tokens and Markdown 24,379, a 19-fold difference. That total is inflated by the short pages, so the seven full articles are the fairer guide.

Other published numbers fit the same pattern. [Cloudflare’s announcement, as reported by PPC Land](https://ppc.land/cloudflare-cuts-ai-token-costs-by-80-with-markdown-conversion/), counted 16,180 HTML tokens against 3,150 Markdown tokens for its own announcement post, about 80% fewer. [Tassos Marinos measured](https://www.tassos.gr/blog/same-joomla-page-with-fewer-tokens-serve-markdown-chatgpt) one Joomla page at 20,347 against 3,270, and [Checkly’s docs page](https://www.checklyhq.com/blog/state-of-ai-agent-content-negotation/) went from 180,573 to 478. The heavier the page around the content, the bigger the saving.

## Where the extra HTML tokens come from

Most of the overhead is theme and plugin code, not your content: inline CSS alone was about 13,500 tokens per page in our test, before a single word of the post. Inline CSS is styling written into the page inside `<style>` tags rather than loaded from a separate file. Twenty Twenty-Five, the block theme on our test site, printed 19 to 28 of these blocks on every page we measured, 13,355 to 13,714 tokens each time.

![Stacked bar chart: an almost empty About page costs 19,558 tokens as HTML, 13,714 of them inline CSS, and 6 as Markdown; the llms.txt validator article costs 27,318 tokens as HTML and 3,497 as Markdown](https://skybootstrap.com/wp-content/uploads/2026/10/html-to-markdown-for-llm-token-breakdown.webp "Token breakdown of two lab pages by tag type. Every page carried roughly the same block of CSS and scripts.")

Our About page shows the floor. It has almost no text, yet its HTML cost 19,558 tokens, against 6 for its Markdown. That includes 13,714 tokens of inline CSS in 19 `<style>` blocks, 2,350 of scripts and about 1,000 of other `<head>` tags. Our test site also ran two SEO plugins, so every page carried two blocks of JSON-LD structured data.

Every page on the site pays this cost again. An agent that reads ten of your pages as HTML reads that CSS ten times.

The bill goes to whoever runs the agent, not to you, but the waste still costs you. A context window is the most tokens a model can take in at once, and agents often work through several pages in one task. About 19,500 of every page’s tokens are theme and plugin code, 13,500 of them CSS. The window fills with markup long before it fills with your content, so fewer of your pages fit into one task.

## Does Markdown lose anything an LLM needs?

Not in our test: the Markdown kept every heading, table and code block from the post, and cost about the same as the page’s plain text, which keeps none of that structure. On the longest article, the plain text of the whole page came to 4,754 tokens and the Markdown to 4,828.

So Markdown’s own syntax is a small cost next to the markup it replaces. The saving comes from dropping markup, scripts and styles, not from Markdown being clever. Markdown is the better choice of the two because a model can still see which line is a heading, which lines belong to a table and where code starts and ends. It also keeps link addresses and image descriptions, which plain text throws away, so an agent can follow a link or tell what a screenshot showed.

## HTML to Markdown for LLMs: which converter keeps the right content?

Convert only the main content, then check the output: on full pages, every converter leaked menus or comment forms. trafilatura, the leanest, dropped each post’s title and image alt text. We ran four common Python routes on the seven articles and compared each with the Markdown from the small plugin in our serving tutorial, which builds it from the post itself. We matched every sentence and heading of eight or more words, and every line of code, against each output.

| Route | Tokens (7 posts) | Lost from the post | Site chrome leaked | Notes |
| --- | --- | --- | --- | --- |
| Purpose-built test plugin (the mu-plugin from our serving tutorial) | 24,108 | Reference | None, by design | Title and alt text kept |
| trafilatura 2.3.1 | 23,528 | H1 title, image alt text | “Leave a Reply” heading | Smallest, partly because of what it dropped |
| markdownify 1.2.3, `<main>` only | 26,635 | Nothing | Comment form, “More posts” | Both sit inside `<main>` in this theme |
| html2text 2025.4.15, full page | 27,992 | Nothing | Skip link, menu, site title, comment form, “More posts” | Turned all 29 code fences into indented code |
| markdownify 1.2.3, full page | 28,122 | Nothing | Skip link, menu, site title, comment form, “More posts” | Added the theme’s headings to the outline |

Site chrome here means the parts of a page that aren’t the post: the skip link, menus, the comment form, related-post lists and the site title. No route kept code language labels, the plugin included.

What that means in practice:

- All the routes land close on tokens. Even full-page conversion came within 17% of the purpose-built version, because every converter drops the CSS and scripts. Converting at all is the big win.
- Chrome is the real cost. A “Leave a Reply” form or a “More posts” list mixed in with your content can confuse a model about what the page is.
- Watch for silent losses. trafilatura was the smallest partly because it threw some content away. A missing title or image description is hard to notice, and a model can’t tell it’s gone.
- Check the options. We first passed `strip=["script", "style"]` to markdownify, which keeps those tags’ text instead of removing it: 141,409 tokens instead of 28,122. The default does the right thing.

## How to give AI agents Markdown from WordPress

If agents read your site, serve a purpose-built Markdown version at the same URL or a `.md` address, so nobody has to convert your HTML at all. Claude Code, Cursor and OpenCode, three coding agents, already put Markdown first in their requests when they fetch a page. Chat assistants still mostly fetch HTML, so this mainly helps agents and tools today.

You have three routes:

- **A plugin or a small piece of code.** It builds Markdown from the post itself, so it knows where the content starts and ends. [Our step-by-step Markdown serving tutorial](https://skybootstrap.com/serve-markdown-to-ai-agents/) has the code and a plugin comparison. If you want it done without code, [our Skyboot AI Markdown plugin](https://skybootstrap.com/skyboot-ai-markdown/) is one of those plugins. It converts headings, lists, tables, links and images from the post and removes shortcodes and breadcrumb widgets, keeping any text inside the shortcodes ([what its Markdown pages contain](https://skybootstrap.com/docs/skyboot-ai-markdown/llms-txt-and-markdown/)).
- **[Cloudflare Markdown for Agents](https://developers.cloudflare.com/fundamentals/reference/markdown-for-agents/).** It converts at the edge when a request asks for Markdown, removing navigation, headers, footers, scripts and styles. It needs a Pro plan or higher.
- **A script on your side.** If you’re feeding pages into your own AI tool, convert only `<main>` and spot-check the output, as above.

Serving two formats at one address needs care with caching, and the fixes for each cache layer are in [our Vary: Accept caching guide](https://skybootstrap.com/cache-vary-accept-header/). Whether an llms.txt file adds anything on top is answered by [the 2026 llms.txt evidence review](https://skybootstrap.com/does-llms-txt-work/), and the wider setup for AI-readable WordPress starts at [the llms.txt for WordPress hub](https://skybootstrap.com/llms-txt-wordpress/). Markdown helps agents read your site; letting them take actions on it is a different job, covered in [what site owners should know about the Abilities API](https://skybootstrap.com/wordpress-abilities-api/).

## Measure your own pages

To measure your own pages, run a short Python script that fetches one URL as HTML and as Markdown, and counts both with OpenAI’s o200k_base tokenizer; it takes about a minute. Your theme will give different numbers from ours, which is the point of measuring. If your site already serves Markdown, through our plugin or another, measure its `.md` address against the HTML page, because that’s what agents will actually get.

1. Install the tokenizer: `pip install tiktoken`.
2. Save the script below as `token_compare.py`.
3. Run `python3 token_compare.py https://yoursite.com/some-post/` (on Windows, `py` instead of `python3`).

```
#!/usr/bin/env python3
"""Compare the tokens an AI agent pays for one page as HTML and as Markdown.
Usage: python3 token_compare.py https://yoursite.com/some-post/
Needs: pip install tiktoken
"""
import sys
import urllib.request

import tiktoken

ENC = tiktoken.get_encoding("o200k_base") # the tokenizer used by GPT-4o-class models
BROWSER = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"

def fetch(url, accept):
req = urllib.request.Request(url, headers={"Accept": accept, "User-Agent": "token-compare/1.0"})
with urllib.request.urlopen(req, timeout=20) as r:
return r.headers.get("Content-Type", ""), r.read().decode("utf-8", "replace")

url = sys.argv[1]
html_type, html = fetch(url, BROWSER)
try:
md_type, md = fetch(url, "text/markdown")
except Exception:
md_type, md = "", ""
if "markdown" not in md_type:
md_url = url.rstrip("/") + ".md"
try:
md_type, md = fetch(md_url, "text/markdown")
except Exception:
md_type = ""
if "markdown" not in md_type:
sys.exit(f"HTML: {len(ENC.encode(html)):,} tokens. No Markdown version found (tried Accept: text/markdown and .md).")

h, m = len(ENC.encode(html)), len(ENC.encode(md))
print(f"HTML: {h:>8,} tokens ({html_type})")
print(f"Markdown: {m:>8,} tokens ({md_type})")
print(f"HTML costs {h / m:.1f}x the Markdown; Markdown saves {1 - m / h:.0%}.")
```

On our test site, the script matched the Markdown count from the tokenizer we used for this study (3,497 tokens). The HTML count was within 0.2%, since parts of the HTML change on every request and the two tokenizer libraries differ slightly.

If your site doesn’t serve Markdown yet, the script reports the HTML count only. To see roughly what a converter would hand an agent, save the page’s `<main>` element as `main.html`, run `pip install markdownify`, then run:

```
python3 -c "import sys,tiktoken,markdownify;print(len(tiktoken.get_encoding('o200k_base').encode(markdownify.markdownify(open(sys.argv[1]).read()))))" main.html
```

## Frequently Asked Questions

### How much does Markdown save over HTML for an LLM?

On seven full WordPress articles, Markdown cut the token cost by 83 to 92%. The HTML pages cost 25,635 to 28,999 tokens, against 2,182 to 4,828 for the same posts in Markdown, counted with OpenAI’s o200k_base tokenizer. Your theme will change the numbers, so measure a page or two of your own.

### Why does a WordPress HTML page cost so many tokens?

Most of the overhead is theme and plugin code, not your content. On our test site, the Twenty Twenty-Five theme printed 13,355 to 13,714 tokens of inline CSS on every page before a single word of the post. Menus, scripts and comment forms add more, and none of that helps an LLM answer a question.

### Which HTML to Markdown converter should I use?

Convert only the main content and check the output. On full pages, every converter we tested leaked menus or comment forms, and trafilatura, the leanest, dropped each post’s title and image alt text. Markdown built from the post itself avoided the leaked menus and the dropped titles in our test, though it still lost code language labels.

## What to do with these numbers

If coding agents read your site, for example on documentation or developer pages, serve them Markdown, using the tutorial’s code or a plugin such as ours. Then measure your own busiest pages with the script above, because your theme sets the overhead.

If your readers mostly arrive from search, fix the page itself first; Markdown mainly helps agents today. Documentation sites can go a step further with [a single llms-full.txt file of their docs](https://skybootstrap.com/llms-full-txt/).

## How we tested

We measured 20 pages on a WordPress 7.1.3 test site with the default Twenty Twenty-Five theme, counting tokens with o200k_base, and compared five conversion routes on the seven full articles.

- Pages: seven full articles of about 1,400 to 3,200 words, plus nine one-paragraph test posts and four short pages (About, Contact, Start here, Privacy policy).
- Forms measured: the full HTML a browser receives, the `<main>` element, Markdown from our serving tutorial’s plugin (not Skyboot AI Markdown), and plain text with tags removed. That plugin is also the reference in the converter test, so it shows no losses and no chrome by design.
- Converters: markdownify, html2text, and trafilatura in Python, with the settings shown above.
- Limits: one block theme, one tokenizer, and a test site running both Rank Math and AIOSEO. Classic themes and page builders will carry different overhead, and other models’ tokenizers will give different counts. We measured cost, not answer quality.